From 5841fd73a937a6c3c48ae88095348d5cab652478 Mon Sep 17 00:00:00 2001 From: chend Date: Tue, 23 Apr 2024 10:07:07 -0400 Subject: [PATCH 01/39] To fix the clicking accuracy issues, the old version(2022) of MIPAV for PickVolum3D were retrieved to replace the current one. --- .../WildMagic/Render/VolumeImage.java | 4784 +++++++++-------- .../WildMagic/VolumeTriPlanarRender.java | 34 + 2 files changed, 2427 insertions(+), 2391 deletions(-) diff --git a/src/gov/nih/mipav/view/renderer/WildMagic/Render/VolumeImage.java b/src/gov/nih/mipav/view/renderer/WildMagic/Render/VolumeImage.java index 704026790d..ef09e08086 100644 --- a/src/gov/nih/mipav/view/renderer/WildMagic/Render/VolumeImage.java +++ b/src/gov/nih/mipav/view/renderer/WildMagic/Render/VolumeImage.java @@ -1,2391 +1,2393 @@ -package gov.nih.mipav.view.renderer.WildMagic.Render; - - -import static java.lang.System.nanoTime; -import static java.lang.System.out; -import gov.nih.mipav.model.algorithms.filters.AlgorithmGradientMagnitudeSep; -import gov.nih.mipav.model.algorithms.filters.OpenCL.filters.OpenCLAlgorithmGradientMagnitude; -import gov.nih.mipav.model.algorithms.filters.OpenCL.filters.OpenCLAlgorithmVolumeNormals; -import gov.nih.mipav.model.file.*; -import gov.nih.mipav.model.structures.*; - -import gov.nih.mipav.view.*; -import gov.nih.mipav.view.dialogs.*; -import gov.nih.mipav.view.renderer.WildMagic.VolumeTriPlanarInterface; - -import java.awt.Dimension; -import java.awt.event.ActionEvent; -import java.io.*; -import java.nio.Buffer; -import java.util.BitSet; -import java.util.Vector; - - -import org.jocl.CL; - -import WildMagic.LibFoundation.Mathematics.ColorRGB; -import WildMagic.LibFoundation.Mathematics.ColorRGBA; -import WildMagic.LibFoundation.Mathematics.Vector2f; -import WildMagic.LibGraphics.Rendering.*; - -/** * - * The VolumeImage class provides an interface between the MIPAV ModelImage and the 2D and 3D Textures used to render - * the ModelImage on the GPU. The VolumeImage creates the supporting Texture and GraphicImage objects that pass - * the ModelImage data to the GPU. It also creates Texture and GraphicsImage objects for the ModelImage Look-up Table (LUT) - * and the ModelImage opacity transfer function. Other textures that are used for advanced volume rendering, such as - * a normal map for surface rendering, and the gradient-magnitude and laplace images for the multi-histogram rendering are - * calculated and passed on-demand to the GPU when the user selects these options in the Volume-Renderer user-interface. - * - * The VolumeImage data structure handles all GPU content for rendering one ModelImage. All Textures and GraphicsImages - * are initialized and stored in the VolumeImage. When needed by the renderer they are loaded onto the GPU. Any images - * that are derived from the ModelImage: Normal map, Gradient Magnitude, Laplace, are either read from file or calculated - * and then written to file. The supporting files are stored in a directory on disk located next to the original ModelImage. - * The directory is named with the ModelImage name followed by "_RenderFiles". - * - * - */ -public class VolumeImage implements Serializable { - /** */ - private static final long serialVersionUID = -7254697711265907746L; - - /** Reference to ModelImage image */ - private ModelImage m_kImage; - private ModelImage m_kImageGM; - - /** GraphicsImage contains GM opacity transfer function data: */ - private GraphicsImage m_kOpacityMap_GM = null; - - /** - * Texture contains texture filter modes and GraphicsImage for opacity transfer function: - */ - private Texture m_kOpacityMapTarget_GM = null; - - /** Data storage for volume: */ - private GraphicsImage[] m_kVolume; - - /** Texture object for data: */ - private Texture m_kVolumeTarget; - - /** Data storage for normals: */ - private GraphicsImage[] m_kNormal; - /** Set to true if the Normal Map has been initialized. */ - private boolean m_bNormalsInit = false; - - /** Texture object for normal map: */ - private Texture m_kNormalMapTarget; - - /** Texture object for GPU computations: */ - private Texture m_kScratchTarget; - - /** Data storage for color map: */ - private GraphicsImage m_kColorMap; - - /** Texture object for color map: */ - private Texture m_kColorMapTarget; - - /** Data storage for volume gradient magnitude: */ - private GraphicsImage[] m_kVolumeGM; - /** Set to true if the Gradient Magnitude texture map has been initialized. */ - private boolean m_bGMInit = false; - - /** Texture object for volume gradient magnitude data: */ - private Texture m_kVolumeGMTarget; - - /** Data storage for surfaces: */ - private GraphicsImage m_kSurfaceImage; - - /** Texture object for surfaces: */ - private Texture m_kSurfaceTarget; - - /** ModelLUT */ - private ModelLUT m_kLUT = null; - - /** ModelRGB */ - private ModelRGB m_kRGBT = null; - - /** Image scale factors for display in 3D */ - private float m_fX = 1, m_fY = 1, m_fZ = 1, m_fMax = 1; - private int m_iMaxExtent = 1; - - /** Image name post-fix typically either 'A' or 'B' */ - private String m_kPostfix = null; - - /** Directory for calculated images */ - private String m_kDir = null; - - /** Histogram data for multi-histogram interface */ - private GraphicsImage[] m_kHisto = null; - /** Set to true when the multi-histogram histogram texture has been initialized. */ - private boolean m_bHistoInit = false; - - /** Texture object for data: */ - private Texture m_kHistoTarget; - - /** Texture coordinates for displaying histogram in 2D */ - private Vector2f[] m_akHistoTCoord = null; - - private float m_fDRRNormalize = 255.0f; - - /** Current position in time (4D data) */ - private int m_iTimeSlice = 0; - - /** Total number of time-slices (4D data) */ - private int m_iTimeSteps = 0; - - /** 3D sub-images (4D data) */ -// private ModelImage[] m_akImages; -// private ModelImage[] m_akImagesGM; - - private Vector2f[] m_akGradientMagMinMax; - - private TransferFunction opacityTransferFn; - - /* Default Constructor */ - public VolumeImage() {} - - /** - * Create a Volume image with the input ModelImage. The supporting images for advanced volume rendering, such as - * the normal map, gradient magnitude and laplace images are generated on-demand and stored in a directory for - * later use. The directory is created if it does not already exist, with the ModelImage name + "_RenderFiles" as - * the directory name. - * - * @param bClone, when true clone the input ModelImage, when false reference the ModelImage - * @param kImage input ModelImage - * @param kPostfix Postfix for images 'A' or 'B' - * @param kProgress progress bar - * @param iProgress progress bar increment - */ - public VolumeImage(boolean bClone, final ModelImage kImage, final String kPostfix, final ViewJProgressBar kProgress, final int iProgress) { - this( bClone, kImage, kPostfix, kProgress, iProgress, true ); - } - - public VolumeImage(boolean bClone, final ModelImage kImage, final String kPostfix, final ViewJProgressBar kProgress, final int iProgress, boolean initGradientMagnitude) { - m_kPostfix = new String(kPostfix); - // clone the input image, in the future this might be a reference. - if ( bClone ) - { - m_kImage = (ModelImage)kImage.clone(); - } - else - { - m_kImage = kImage; - } - // Initialize the Texture maps. - init(kProgress, iProgress, initGradientMagnitude); - } - - /** - * Copy the data from the input GraphicsImage and return a new ModelImage of that data. - * Any changes to the GraphicsImage that occur only on the GPU can first be written from - * the GPU back into the GraphicsImage CPU data storage. This enables calculations that - * are performed on the GPU to be written back into a ModelImage data structure. - * - * @param kImage Graphics Image to copy - * @param bSwap when true convert from RGBA (graphics format) to ARGB (ModelImage format) - * @return new ModelImage from Volume Texture on GPU. - */ - public static ModelImage CreateImageFromTexture(final GraphicsImage kImage, final boolean bSwap) { - final int iXBound = kImage.GetBound(0); - final int iYBound = kImage.GetBound(1); - final int iZBound = kImage.GetBound(2); - final int iSize = iXBound * iYBound * iZBound; - final int[] extents = new int[] {iXBound, iYBound, iZBound}; - - ModelImage kResult = null; - if (kImage.GetFormat() == GraphicsImage.FormatMode.IT_RGBA8888) { - byte[] aucData = kImage.GetData(); - if (bSwap) { - byte bVal = 0; - aucData = new byte[4 * iXBound * iYBound * iZBound]; - for (int i = 0; i < iSize; i += 4) { - if (kImage.GetData()[i + 1] > bVal) { - bVal = kImage.GetData()[i + 1]; - } - aucData[i] = kImage.GetData()[i + 3]; - aucData[i + 1] = kImage.GetData()[i + 1]; - aucData[i + 2] = kImage.GetData()[i + 2]; - aucData[i + 3] = kImage.GetData()[i]; - //System.err.println( kImage.GetData()[i + 3] + " " + kImage.GetData()[i + 1] + " " + kImage.GetData()[i + 2] ); - } - // System.err.println( bVal ); - } - try { - kResult = new ModelImage(ModelStorageBase.ARGB, extents, ""); - kResult.importData(0, aucData, true); - } catch (final IOException e) { - e.printStackTrace(); - } - } else { - final byte[] aiImageData = kImage.GetData(); - try { - kResult = new ModelImage(ModelStorageBase.UBYTE, extents, ""); - kResult.importData(0, aiImageData, true); - } catch (final IOException e) { - e.printStackTrace(); - } - } - return kResult; - } - - /** - * Initialize the textures for the color lookup table. - * - * @param kLUT the new LUT. - * @param kRGBT the new RGB table. - * @param kPostfix the string postfix to concatenate to the "ColorMap" image name. - * @return GraphicsImage, the new GraphicsImage storing the colormap lookup table. - */ - public static GraphicsImage InitColorMap( Texture kTexture, GraphicsImage kImage, final ModelStorageBase kLUT, final String kPostFix) { - byte[] aucData; - if ( kImage == null ) - { - aucData = new byte[256 * 4]; - } - else - { - aucData = kImage.GetData(); - } - if (kLUT instanceof ModelLUT ) { - // ModelImage is Color, initialize the ModelRGB - ModelLUT.exportIndexedLUTMin((ModelLUT)kLUT, aucData); - } - else if (kLUT instanceof ModelRGB ) { - // Initialize the ModelLUT - ModelLUT.exportIndexedLUTMin((ModelRGB)kLUT, aucData); - } - if ( kImage == null ) - { - // Return the new GraphicsImage containing the table data: - return new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, 256, aucData, new String("ColorMap" + kPostFix)); - } - if ( kTexture != null ) - { - kTexture.Reload(true); - } - return kImage; - } - - - private GraphicsImage initColorMap() { - final byte[] aucData = new byte[256 * 4]; - if (m_kRGBT != null) - { - // ModelImage is Color, initialize the ModelRGB - ModelLUT.exportIndexedLUTMin(m_kRGBT, aucData); - } else if ( m_kLUT != null ) - { - // Initialize the ModelLUT - ModelLUT.exportIndexedLUTMin(m_kLUT, aucData); - } - // Return the new GraphicsImage containing the table data: - return new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, 256, 1, aucData, new String("ColorMap" + m_kImage.getImageName() + m_kPostfix)); - } - - - /** - * When a ModelImage changes on the CPU, this function is used to update the ModelImage - * data on the CPU. - * - * @param kImage Modified ModelImage to copy into the GPU Texture and GraphicsImage - * @param iTimeSlice time value for 4D image, 0 otherwise - * @param kNewImage a new ModelImage (always 3D) that the data or data subset for 4D image can be copied into (when non-null). - * @param kVolumeImage GraphicsImage that will hold the ModelImage data - * @param kVolumeTexture Texture object containing the GraphicsImage - * @param kImageName new image name for the new ModelImage. - * @param bSwap when true swap the ARGB (ModelImage) color data representation to a RGBA (GPU) color representation. - * @return - */ - public static GraphicsImage UpdateData(final ModelImage kImage, final int iTimeSlice, final ModelImage kNewImage, - final GraphicsImage kVolumeImage, final Texture kVolumeTexture, final String kImageName, - final boolean bSwap, final boolean bRescale) { - GraphicsImage kReturn = kVolumeImage; - final int iXBound = kImage.getExtents()[0]; - final int iYBound = kImage.getExtents()[1]; - final int iZBound = kImage.getExtents()[2]; - - byte[] aucData = null; - int iSize = iXBound * iYBound * iZBound; - if (kImage.isColorImage()) { - iSize *= 4; - aucData = new byte[iSize]; - try { - kImage.exportDataUseMask(iTimeSlice * iSize, iSize, bRescale, aucData); - if (bSwap) { - for (int i = 0; i < iSize; i += 4) { - final byte tmp = aucData[i]; - aucData[i] = aucData[i + 1]; - aucData[i + 1] = aucData[i + 2]; - aucData[i + 2] = aucData[i + 3]; - aucData[i + 3] = tmp; - } - } - } catch (final IOException e) { - e.printStackTrace(); - } - if (kReturn == null) { - kReturn = new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, aucData, - kImageName); - } else { - kReturn.SetData(aucData, iXBound, iYBound, iZBound); - } - } else { - aucData = new byte[iSize]; - try { - kImage.exportDataUseMask(iTimeSlice * iSize, iSize, bRescale, aucData); - // Temporary make the texture an RGBA texture until JOGL2 fixes NPOT textures. - byte[] aucData2 = new byte[iSize*4]; - for (int i = 0; i < iSize; i++) { - aucData2[i * 4 + 0] = aucData[i]; - aucData2[i * 4 + 1] = aucData[i]; - aucData2[i * 4 + 2] = aucData[i]; - aucData2[i * 4 + 3] = 1; - } - - if (kReturn == null) { - kReturn = new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, aucData2, - kImageName); - } else { - kReturn.SetData(aucData2, iXBound, iYBound, iZBound); - } - } catch (final IOException e) { - e.printStackTrace(); - } - - } - if (kNewImage != null) { - try { - kNewImage.importData(0, aucData, true); - } catch (final IOException e) {} - } - if (kVolumeTexture != null) { - kVolumeTexture.Reload(true); - } - return kReturn; - } - - private GraphicsImage initVolumeData(final ModelImage kImage, final int iTimeSlice, - final Texture kVolumeTexture, final String kImageName, final boolean bSwap, final boolean bRescale) - { - GraphicsImage kReturn = null; - final int iXBound = kImage.getExtents()[0]; - final int iYBound = kImage.getExtents()[1]; - final int iZBound = kImage.getExtents()[2]; - - byte[] aucData = null; - int iSize = iXBound * iYBound * iZBound; - if (kImage.isColorImage()) { - iSize *= 4; - aucData = new byte[iSize]; - try { - kImage.exportDataUseMask(iTimeSlice * iSize, iSize, bRescale, aucData); - if (bSwap) { - for (int i = 0; i < iSize; i += 4) { - final byte tmp = aucData[i]; - aucData[i] = aucData[i + 1]; - aucData[i + 1] = aucData[i + 2]; - aucData[i + 2] = aucData[i + 3]; - aucData[i + 3] = tmp; - } - } - } catch (final IOException e) { - e.printStackTrace(); - } - kReturn = new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, aucData, - kImageName); - } else { - aucData = new byte[iSize]; - try { - kImage.exportDataUseMask(iTimeSlice * iSize, iSize, bRescale, aucData); - // Temporary make the texture an RGBA texture until JOGL2 fixes NPOT textures. - byte[] aucData2 = new byte[iSize*4]; - for (int i = 0; i < iSize; i++) { - aucData2[i * 4 + 0] = aucData[i]; - aucData2[i * 4 + 1] = aucData[i]; - aucData2[i * 4 + 2] = aucData[i]; - aucData2[i * 4 + 3] = 1; - } - - kReturn = new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, aucData2, - kImageName); - } catch (final IOException e) { - e.printStackTrace(); - } - - } - if (kVolumeTexture != null) { - kVolumeTexture.Reload(true); - } - return kReturn; - } - - private GraphicsImage resetVolumeData(final ModelImage kImage, final int iTimeSlice, GraphicsImage kGraphicsImage, - final Texture kVolumeTexture, final String kImageName, final boolean bSwap, final boolean bRescale) - { - final int iXBound = kImage.getExtents()[0]; - final int iYBound = kImage.getExtents()[1]; - final int iZBound = kImage.getExtents()[2]; - int iSize = iXBound * iYBound * iZBound; - - byte[] aucData = null; - if (kImage.isColorImage()) { - iSize *= 4; - aucData = kGraphicsImage.GetData(); - try { - kImage.exportDataUseMask(iTimeSlice * iSize, iSize, bRescale, aucData); - if (bSwap) { - for (int i = 0; i < iSize; i += 4) { - final byte tmp = aucData[i]; - aucData[i] = aucData[i + 1]; - aucData[i + 1] = aucData[i + 2]; - aucData[i + 2] = aucData[i + 3]; - aucData[i + 3] = tmp; - } - } - } catch (final IOException e) { - e.printStackTrace(); - } - } - else { - aucData = new byte[iSize]; - try { - kImage.exportDataUseMask(iTimeSlice * iSize, iSize, bRescale, aucData); - byte[] aucData2 = kGraphicsImage.GetData(); - for (int i = 0; i < iSize; i++) { - aucData2[i * 4 + 0] = aucData[i]; - aucData2[i * 4 + 1] = aucData[i]; - aucData2[i * 4 + 2] = aucData[i]; - aucData2[i * 4 + 3] = 1; - } - } catch (final IOException e) { - e.printStackTrace(); - } - - } - if (kVolumeTexture != null) { - kVolumeTexture.Reload(true); - } - return kGraphicsImage; - } - - private void addNormals(final ModelImage kImage, final int iTimeSlice) { - final int iXBound = kImage.getExtents()[0]; - final int iYBound = kImage.getExtents()[1]; - final int iZBound = kImage.getExtents()[2]; - - int iSize = iXBound * iYBound * iZBound; - byte[] aucData = new byte[iSize * 4]; - try { - kImage.exportDataUseMask(0, iSize * 4, true, aucData); - byte[] volumeData = m_kVolume[iTimeSlice].GetData(); - for (int i = 0; i < iSize; i++) { - volumeData[i*4 + 1] = aucData[i*4 + 1]; - volumeData[i*4 + 2] = aucData[i*4 + 2]; - volumeData[i*4 + 3] = aucData[i*4 + 3]; - } - } catch (final IOException e) { - e.printStackTrace(); - } - m_kVolumeTarget.Reload(true); - } - - - private GraphicsImage createGM_Laplace(final ModelImage kImageGM, final ModelImage kImageL, - final GraphicsImage kVolumeImage, - final int iTimeSlice, final boolean bSwap) { - - GraphicsImage kReturn = kVolumeImage; - final int iXBound = kImageGM.getExtents()[0]; - final int iYBound = kImageGM.getExtents()[1]; - final int iZBound = kImageGM.getExtents()[2]; - - int iSize = iXBound * iYBound * iZBound; - byte[] aucDataL = new byte[iSize]; - - if ( kImageL != null ) - { - try { - kImageL.exportDataUseMask(0, iSize, false, aucDataL); - } catch (final IOException e) { - e.printStackTrace(); - } - } - - byte[] aucDataGM = null; - if (kImageGM.isColorImage()) { - iSize *= 4; - aucDataGM = new byte[iSize]; - try { - kImageGM.exportDataUseMask(0, iSize, false, aucDataGM); - if (bSwap) { - for (int i = 0, j = 0; i < iSize; i += 4) { - aucDataGM[i] = aucDataGM[i + 1]; - aucDataGM[i + 1] = aucDataGM[i + 2]; - aucDataGM[i + 2] = aucDataGM[i + 3]; - if ( kImageL != null ) - { - aucDataGM[i + 3] = aucDataL[j++]; - } - } - } - kImageGM.importData( 0, aucDataGM, false ); - } catch (final IOException e) { - e.printStackTrace(); - } - if (kReturn == null) { - kReturn = new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, aucDataGM, - kImageGM.getImageName()); - } else { - kReturn.SetData(aucDataGM, iXBound, iYBound, iZBound); - } - } - else - { - try { - aucDataGM = new byte[iSize]; - kImageGM.exportDataUseMask(0, iSize, false, aucDataGM); - byte[] aucData2 = new byte[iSize*4]; - for (int i = 0; i < iSize; i++) { - aucData2[i * 4 + 0] = aucDataGM[i]; - aucData2[i * 4 + 1] = aucDataGM[i]; - aucData2[i * 4 + 2] = aucDataGM[i]; - if ( kImageL != null ) - { - aucData2[i * 4 + 3] = aucDataL[i]; - } - } - - if (kReturn == null) { - kReturn = new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, aucData2, - kImageGM.getImageName()); - } else { - kReturn.SetData(aucData2, iXBound, iYBound, iZBound); - } - } catch (final IOException e) { - e.printStackTrace(); - } catch ( java.lang.OutOfMemoryError e ) { - return null; - } - aucDataGM = null; - } - return kReturn; - } - - - - /** - * Creates a new GraphicsImage for the input ModelSimpleImage. The ModelSimpleImage data is - * referenced by the new GraphicsImage and will be passed to the GPU as a texture. - * @param kImage input ModelSimpleImage. - * @param kImageName name for the GraphicsImage. - * @return a new GraphcisImage. - */ - public static GraphicsImage UpdateData(final ModelSimpleImage kImage, final String kImageName) { - final GraphicsImage.FormatMode eType = GraphicsImage.FormatMode.IT_L32F; - - if (kImage.nDims == 3) { - return new GraphicsImage(eType, kImage.extents[0], kImage.extents[1], kImage.extents[2], kImage.data, - kImageName); - } - return new GraphicsImage(eType, kImage.extents[0], kImage.extents[1], 1, kImage.data, kImageName); - } - - /** - * When the LUT changes, this function updates the LUT data on the GPU. - * - * @param kColorTexture the color-map Texture object. - * @param kColorMap the color-map GraphicsImage object (stores data). - * @param kLUT the updated or new LUT. - */ - public static void UpdateImages(final Texture kColorTexture, final GraphicsImage kColorMap, final ModelLUT kLUT) { - if (kLUT == null) { - return; - } - ModelLUT.exportIndexedLUTMin(kLUT, kColorMap.GetData()); - kColorTexture.Reload(true); - } - - /** - * When the ModelImage data is rendered as a solid surface, the Normal map is used in the rendering. - * The Normal map is calculated on the GPU by one of the GLSL shader programs. This function is called - * after the GPU calculation has finished and the GPU data has been copied into a new ModelImage on the CPU - * the new ModelImage then contains the Normal map information, which is written into a file and - * copied into the Normal map GraphicsImage used to render the original ModelImage. - * - * @param i current 3D sub-image for 4D data. If the data is 3D this value should be 0. - * @param kImage a new ModelImage containing the calculated Normals. - */ - public void CopyNormalFiles(final int i, final ModelImage kImage) { - final String kImageName = ModelImage.makeImageName(m_kImage.getFileInfo(0).getFileName(), "_Normal_" - + i); - JDialogBase.updateFileInfo( m_kImage, kImage ); - ModelImage.saveImage( kImage, kImageName + ".xml", m_kDir, false ); - if ( m_kImage.isColorImage() ) - { - m_kNormal[i] = initVolumeData(kImage, 0, m_kNormalMapTarget, kImage.getImageName(), true, true); - } - else - { - addNormals(kImage, i); - } - } - - /** - * Read the current Volume Texture from the GPU and return a new ModelImage of that data. - * - * @return new ModelImage from Volume Texture on GPU. - */ - public static ModelImage CreateImageFromTexture(final GraphicsImage kImage) { - final int[] extents = new int[ kImage.GetDimension() ]; - for ( int i = 0; i < extents.length; i++ ) - { - extents[i] = kImage.GetBound(i); - } - - //GraphicsImage.Type eType = kImage.GetType(); - int type = ModelStorageBase.ARGB; - /* - switch ( eType ) - { - case IT_BYTE : type = ModelStorageBase.BYTE; break; - case IT_UBYTE : type = ModelStorageBase.UBYTE; break; - case IT_SHORT : type = ModelStorageBase.SHORT; break; - case IT_USHORT : type = ModelStorageBase.USHORT; break; - case IT_INT : type = ModelStorageBase.INTEGER; break; - case IT_UINT : type = ModelStorageBase.UINTEGER; break; - case IT_LONG : type = ModelStorageBase.LONG; break; - case IT_FLOAT : type = ModelStorageBase.FLOAT; break; - case IT_DOUBLE : type = ModelStorageBase.DOUBLE; break; - } - */ - final ModelImage kResult = new ModelImage(type, extents, kImage.GetName() ); - int size = kImage.GetQuantity(); - for ( int i = 0; i < size;i++ ) - { - kResult.set(i*4 + 1, kImage.GetData()[i*3 + 0]); - kResult.set(i*4 + 2, kImage.GetData()[i*3 + 1]); - kResult.set(i*4 + 3, kImage.GetData()[i*3 + 2]); - } - return kResult; - } - - /** - * Memory cleanup. - */ - public void dispose() { - if (m_kImage == null) { - return; - } - m_kImage.disposeLocal(); - m_kImage = null; - - for (final GraphicsImage element : m_kVolume) { - element.dispose(); - } - m_kVolume = null; - m_kVolumeTarget.dispose(); - m_kVolumeTarget = null; - - if ( m_kNormal != null ) - { - for (final GraphicsImage element : m_kNormal) { - element.dispose(); - } - m_kNormal = null; - } - if ( m_kNormalMapTarget != null ) - { - m_kNormalMapTarget.dispose(); - m_kNormalMapTarget = null; - } - - m_kScratchTarget.dispose(); - m_kScratchTarget = null; - - m_kColorMap.dispose(); - m_kColorMap = null; - m_kColorMapTarget.dispose(); - m_kColorMapTarget = null; - - if ( m_kImageGM != null ) - { - m_kImageGM.disposeLocal(); - m_kImageGM = null; - } - - for (final GraphicsImage element : m_kVolumeGM) { - if ( element != null ) - element.dispose(); - } - m_kVolumeGM = null; - m_kVolumeGMTarget.dispose(); - m_kVolumeGMTarget = null; - - - m_kOpacityMap_GM.dispose(); - m_kOpacityMap_GM = null; - m_kOpacityMapTarget_GM.dispose(); - m_kOpacityMapTarget_GM = null; - - if (m_kSurfaceImage != null) { - m_kSurfaceImage.dispose(); - m_kSurfaceImage = null; - m_kSurfaceTarget.dispose(); - m_kSurfaceTarget = null; - } - - m_kLUT = null; - m_kPostfix = null; - if ( m_kHisto != null ) - { - for (final GraphicsImage element : m_kHisto) { - if ( element != null ) - { - element.dispose(); - } - } - m_kHisto = null; - } - m_akHistoTCoord = null; - } - - - /** - * This function is called when the user selects the Surface or Composite Surface volume rendering option. - * If the normals have already been initialized the function returns. Otherwise the function checks if the - * normals are available in a file on disk, and if so if they match the parameters (size, units, resolutions) of - * the original ModelImage. If the files match they are used and the Normal map is read from file. Otherwise this - * function launches the GPU-based Normal calculation. That calculation when finished calls the CopyNormalFiles - * which writes the calculated normals to disk and updates the Normal map on the GPU for rendering. - */ - public void GenerateNormalFiles( VolumeTriPlanarInterface parentFrame ) { - if ( m_bNormalsInit ) - { - return; - } - if ( !m_bNormalsInit ) - { - int dimX = m_kImage.getExtents().length > 0 ? m_kImage.getExtents()[0] : 1; - int dimY = m_kImage.getExtents().length > 1 ? m_kImage.getExtents()[1] : 1; - int dimZ = m_kImage.getExtents().length > 2 ? m_kImage.getExtents()[2] : 1; - ModelImage outputImage = new ModelImage( ModelStorageBase.ARGB_FLOAT, new int[]{dimX,dimY,dimZ}, "temp" ); - for (int i = 0; i < m_iTimeSteps; i++) { - OpenCLAlgorithmVolumeNormals oclNormals = new OpenCLAlgorithmVolumeNormals( m_kImage, outputImage, CL.CL_DEVICE_TYPE_GPU ); - oclNormals.setTime(i); - oclNormals.run(); - if ( m_kImage.isColorImage() ) - { - m_kNormal[i] = initVolumeData(outputImage, 0, m_kNormalMapTarget, outputImage.getImageName(), true, true); - } - else - { - addNormals(outputImage, i); - } - } - m_bNormalsInit = true; - - if ( m_kNormal != null) - { - m_kNormalMapTarget.SetImage(m_kNormal[m_iTimeSlice]); - m_kNormalMapTarget.Reload(true); - } - outputImage.disposeLocal(); - } - } - - /** - * Return the Color Map Texture. - * @return Volume color map Texture. - */ - public Texture GetColorMapTarget() { - return m_kColorMapTarget; - } - - /** - * Return the normalization factor for DDR rendering mode. - * @return normalization factor for DDR rendering mode. - */ - public float getDRRNorm() { - return m_fDRRNormalize; - } - - /** - * Return the Gradient Magnitude Texture. - * @return Gradient Magnitude Texture. - */ - public Texture GetGradientMapTarget() { - return m_kVolumeGMTarget; - } - - /** - * Returns true if the multi-histogram histogram texture has been initialized, false otherwise. - * @return true if the multi-histogram histogram texture has been initialized, false otherwise. - */ - public boolean isHistoInit() - { - return m_bHistoInit; - } - - /** - * Returns the multi-histogram histogram Texture. - * @return the multi-histogram histogram Texture. - */ - public Texture GetHistoTarget() { - return m_kHistoTarget; - } - - -// private ModelImage[] m_akHistogram; -// public ModelImage GetHistogram() { -// -// if ( !m_bHistoInit ) -// { -// SetGradientMagnitude(null, true, m_kPostfix); -// } -// if ( m_akHistogram == null ) -// { -// m_akHistogram = new ModelImage[m_akImages.length]; -// } -// if ( m_akHistogram[m_iTimeSlice] == null ) -// { -// m_akHistogram[m_iTimeSlice] = new ModelImage(ModelStorageBase.INTEGER, new int[]{256,256}, "JointHisto" + m_iTimeSlice); -// try { -// m_akHistogram[m_iTimeSlice].importData(m_kHisto[m_iTimeSlice].GetData()); -// } catch (IOException e) { -// e.printStackTrace(); -// } -// } -// return m_akHistogram[m_iTimeSlice]; -// } - - /** - * Return the texture coordinates for the multi-histogram histogram texture. - * @return the texture coordinates for the multi-histogram histogram texture. - */ - public Vector2f[] GetHistoTCoords() { - return m_akHistoTCoord; - } - - public ModelImage GetGradientMagnitudeImage() - { - return m_kImageGM; - } - - public Vector2f GetGradientMagnitudeMinMax() - { - return m_akGradientMagMinMax[m_iTimeSlice]; - } - - public float GetGradientMagnitudeMin() - { - return m_akGradientMagMinMax[m_iTimeSlice].X; - } - - public float GetGradientMagnitudeMax() - { - return m_akGradientMagMinMax[m_iTimeSlice].Y; - } - - /** - * Return the ModelImage volume data. - * @return ModelImage volume data. - */ - public ModelImage GetImage() { - return m_kImage; - } - - /** - * Return the ModelImage LUT. - * @return Volume LUT. - */ - public ModelLUT GetLUT() { - return m_kLUT; - } - - /** - * Return the Normal map Texture. - * @return Normal map Texture. - */ - public Texture GetNormalMapTarget() { - return m_kNormalMapTarget; - } - - public Texture GetScratchTarget() { - return m_kScratchTarget; - } - - /** - * Return the gradient magnitude opacity transfer function Texture. - * @return gradient magnitude opacity transfer function Texture. - */ - public Texture GetOpacityMapGMTarget() { - return m_kOpacityMapTarget_GM; - } - - /** - * Return the postfix for this VolumeImage. - * @return postfix for this VolumeImage. - */ - public String GetPostfix() { - return m_kPostfix; - } - - /** - * Return the Volume RGBT. - * @return Volume RGBT. - */ - public ModelStorageBase getLUT() { - return (m_kImage != null ) ? m_kImage.isColorImage() ? m_kRGBT : m_kLUT : null; - } - - /** - * Return the Volume RGBT. - * @return Volume RGBT. - */ - public ModelRGB GetRGB() { - return m_kRGBT; - } - - public float GetTransferedValue( int x, int y, int z ) - { - int dimX = m_kImage.getExtents().length > 0 ? m_kImage.getExtents()[0] : 1; - int dimY = m_kImage.getExtents().length > 1 ? m_kImage.getExtents()[1] : 1; - int dimZ = m_kImage.getExtents().length > 2 ? m_kImage.getExtents()[2] : 1; - if ( x < 0 || x >= dimX || y < 0 || y >= dimY || z < 0 || z >= dimZ ) return -1; - - if ( m_kImage.isColorImage() ) { - float r = m_kRGBT.getROn() ? TransferValue(m_kImage.getFloat(x, y, z, 1)) : -1; - float g = m_kRGBT.getGOn() ? TransferValue(m_kImage.getFloat(x, y, z, 2)) : -1; - float b = m_kRGBT.getBOn() ? TransferValue(m_kImage.getFloat(x, y, z, 3)) : -1; - return Math.max( r, Math.max(g, b)); - } - float value = m_kImage.getFloat(x, y, z); - return TransferValue(value); - } - - private float TransferValue(float value) { - float min = (float) m_kImage.getMin(); - float max = (float) m_kImage.getMax(); - float diff = max - min; - byte index = 0; - if ( (diff > 1) && (diff <= 255) ) - { - index = (byte)(((value - min)/diff) * diff); - } - else - { - index = (byte)(((value - min)/diff) * 255); - } - if ( (index >= 0) && (index < 255) && (m_kColorMap != null) && (m_kColorMap.GetData() != null) ) - { - byte r = m_kColorMap.GetData()[index * 4 + 0]; - byte g = m_kColorMap.GetData()[index * 4 + 1]; - byte b = m_kColorMap.GetData()[index * 4 + 2]; - byte a = m_kColorMap.GetData()[index * 4 + 3]; - return Math.max(r*a, Math.max(g*a, b*a)); - } - return -1; - } - - - /** - * The ModelImage Volume max-scale factor. - * @return Volume max-scale factor. - */ - public float GetScaleMax() { - return m_fMax; - } - - public int GetMaxExtent() - { - return m_iMaxExtent; - } - - /** - * The ModelImage Volume x-scale factor. - * @return Volume x-scale factor. - */ - public float GetScaleX() { - return m_fX; - } - - /** - * The ModelImage Volume y-scale factor. - * @return Volume y-scale factor. - */ - public float GetScaleY() { - return m_fY; - } - - /** - * The ModelImage Volume z-scale factor. - * @return Volume z-scale factor. - */ - public float GetScaleZ() { - return m_fZ; - } - - /** - * Return the surface mask Texture. - * @return surface mask Texture. - */ - public Texture GetSurfaceTarget() { - return m_kSurfaceTarget; - } - - - - /** A vector of BitSet masks, one for each surface loaded into the viewer. */ - protected Vector surfaceMask; - /** A vector of the mask names, so they can be accessed by name: */ - protected Vector surfaceNames; - /** A vector of BitSet masks, one for each surface loaded into the viewer. */ - protected Vector surfaceColor; - /** - * Add a new surface mask. - * @param name surface name. - * @param mask surface mask volume. - */ - public void setSurfaceMask(String name, ColorRGB color, BitSet mask) - { - if ( surfaceMask == null ) - { - surfaceMask = new Vector(); - surfaceNames = new Vector(); - surfaceColor = new Vector(); - } - surfaceMask.add(mask); - surfaceNames.add(name); - surfaceColor.add(color); - updateMask(); - } - - private void updateMask() - { - boolean bUpdate = false; - final int iXBound = m_kImage.getExtents()[0]; - final int iYBound = m_kImage.getExtents()[1]; - final int iZBound = m_kImage.getExtents()[2]; - int length = iXBound * iYBound * iZBound; - for ( int i = 0; i < length; i++ ) - { - boolean color = false; - for ( int surface = 0; surface < surfaceMask.size(); surface++ ) - { - if ( surfaceMask.elementAt(surface).get(i) ) - { - m_kSurfaceImage.GetData()[i * 4 + 0] = (byte) (surfaceColor.elementAt(surface).R * 255); - m_kSurfaceImage.GetData()[i * 4 + 1] = (byte) (surfaceColor.elementAt(surface).G * 255); - m_kSurfaceImage.GetData()[i * 4 + 2] = (byte) (surfaceColor.elementAt(surface).B * 255); - m_kSurfaceImage.GetData()[i * 4 + 3] = (byte) (255); - bUpdate = true; - color = true; - } - } - if ( !color ) - { - m_kSurfaceImage.GetData()[i * 4 + 0] = (byte) (0); - m_kSurfaceImage.GetData()[i * 4 + 1] = (byte) (0); - m_kSurfaceImage.GetData()[i * 4 + 2] = (byte) (0); - m_kSurfaceImage.GetData()[i * 4 + 3] = (byte) (0); - } - } - if ( bUpdate ) - { - m_kSurfaceTarget.Reload(true); - } - } - - /** - * Delete the surface mask, using the name of the mask as reference. - * @param name the surface name. - */ - public void removeSurfaceMask(String name) - { - boolean bUpdate = false; - if ( surfaceMask != null && surfaceNames != null) - { - if ( surfaceNames.contains(name) ) - { - surfaceMask.remove( surfaceNames.indexOf(name) ); - surfaceColor.remove( surfaceNames.indexOf(name) ); - surfaceNames.remove(name); - bUpdate = true; - } - } - if ( bUpdate ) - { - updateMask(); - m_kSurfaceTarget.Reload(true); - } - } - - /** - * Delete the surface mask, using the name of the mask as reference. - * @param name the surface name. - */ - public void setSurfaceMaskColor(String name, ColorRGB color) - { - boolean bUpdate = false; - if ( surfaceMask != null && surfaceNames != null) - { - if ( surfaceNames.contains(name) ) - { - surfaceColor.elementAt( surfaceNames.indexOf(name) ).Copy(color); - bUpdate = true; - } - } - if ( bUpdate ) - { - updateMask(); - } - } - - - /** - * Returns the current rendered time-slice for 4D images. Otherwise returns 0. - * @return the current rendered time-slice for 4D images. Otherwise returns 0. - */ - public int GetTimeSlice() { - return m_iTimeSlice; - } - - /** - * Return the Texture containing the volume data. - * @return Texture containing the volume data. - */ - public Texture GetVolumeTarget() { - return m_kVolumeTarget; - } - - /** - * Return the Buffer containing the volume data, which is stored in the Texture GrapicsImage. - * @return Buffer containing the volume data. - */ - public Buffer GetVolumeTargetBuffer() { - return m_kVolumeTarget.GetImage().GetDataBuffer(); - } - - /** - * Initialize the GraphicsImage for the opacity lookup table. - * - * @param kImage the ModelImage the opacity transfer function applies to. - * @param kPostfix the string postfix to concatenate to the "OpacityMap" image name. - * @return GraphicsImage, the new GraphicsImage storing opacity lookup table. - */ - public GraphicsImage InitOpacityMap(final ModelImage kImage, final String kPostFix) { - final int iLutHeight = 256; - final float[] afData = new float[iLutHeight]; - final float fRange = (float) (kImage.getMax() - kImage.getMin()); - final float fStep = fRange / iLutHeight; - float fDataValue = (float) kImage.getMin(); - for (int i = 0; i < iLutHeight; i++) { - afData[i] = (float) (iLutHeight * (kImage.getMax() - fDataValue) / fRange); - fDataValue += fStep; - } - - return new GraphicsImage(GraphicsImage.FormatMode.IT_L8, iLutHeight, afData, - new String("OpacityMap" + kPostFix)); - } - - /** - * Return true if the Volume image is a color image. - * - * @return true if the Volume image is a color image. - */ - public boolean IsColorImage() { - return m_kImage.isColorImage(); - } - - /** - * Release the Textures containing the volume data. Once Textures are released, they will be re-loaded onto the GPU - * during the next frame. - */ - public void ReleaseVolume() { - m_kVolumeTarget.Reload(true); - } - - - - /** - * Called when the user selects the Gradient Magnitude option or the Multi-Histogram option - * in the Volume Renderer. - * @param kGradientMagnitude pre-computed GradientMagnitude image or null - * @param bComputeLaplace when true the Laplace image and multi-histogram histogram Textures are computed. - * @param kPostfix GraphicsImage postfix string. - */ - public void SetGradientMagnitude(ModelImage kGradientMagnitude, boolean bComputeLaplace, String kPostfix ) - { - int start = 0; - int end = m_kImage.getNDims() > 3 ? m_iTimeSteps : 1; - if ( !m_bGMInit ) - { - try { - // System.err.println( "SetGradientMagnitude " + 0 ); - m_kImageGM = getGradientMagnitude( m_kImage, 0 ); - if ( m_kImageGM != null ) - { - m_kVolumeGM[0] = createGM_Laplace(m_kImageGM, null, m_kVolumeGM[0], 0, true); - if ( m_kVolumeGM[0] != null ) - { - m_akGradientMagMinMax[0] = new Vector2f( (float)m_kImageGM.getMin(), (float)m_kImageGM.getMax() ); - m_kVolumeGMTarget.SetImage(m_kVolumeGM[0]); - m_kVolumeGMTarget.Reload(true); - m_bGMInit = true; - } - } - for (int i = 1; i < m_iTimeSteps; i++) - { - // System.err.println( "SetGradientMagnitude " + i ); - ModelImage gmImage = getGradientMagnitude( m_kImage, i ); - if ( gmImage != null ) - { - m_kVolumeGM[i] = createGM_Laplace(gmImage, null, m_kVolumeGM[i], i, true); - if ( m_kVolumeGM[i] != null ) - { - m_akGradientMagMinMax[i] = new Vector2f( (float)gmImage.getMin(), (float)gmImage.getMax() ); - gmImage.disposeLocal(); - end = i + 1; - } - } - } - } catch ( java.lang.OutOfMemoryError e ) {} - } - - if ( m_bGMInit && bComputeLaplace && (m_kImageGM != null)) - { - GenerateHistogram(m_kVolume, m_kVolumeGM, kPostfix, start, end ); - } - } - - /** - * Sets the ModelRGB for the iImage. - * - * @param kRGBT new ModelRGB - */ - public void SetRGBT(final ModelRGB kRGBT) { - if (kRGBT == null) { - return; - } - ModelLUT.exportIndexedLUTMin(kRGBT, m_kColorMap.GetData()); - m_kColorMapTarget.Reload(true); - m_kRGBT = kRGBT; - } - - /** - * Sets the time slices for 4D data. - * @param iSlice new time slice value. - */ - public void SetTimeSlice(final int iSlice) { - if (m_iTimeSlice != iSlice) { - m_iTimeSlice = iSlice; - update4D(); - } - } - - /** - * Updates the current time slice. - * @param bForward when true the time advances on step forward or wraps to the beginning. - * When false the time moves backward. - */ - public void update4D(final boolean bForward) { - if (m_iTimeSteps == 1) { - return; - } - if (bForward) { - m_iTimeSlice++; - } else { - m_iTimeSlice--; - } - if (m_iTimeSlice >= m_iTimeSteps) { - m_iTimeSlice = 0; - } - if (m_iTimeSlice < 0) { - m_iTimeSlice = m_iTimeSteps - 1; - } - - update4D(); - } - - /** - * Update the image data. - * - * @param kImage the modified ModelImage - * @param bCopytoCPU when true the data is copied from the GPU GraphicsImage into the ModelImage - */ - public void UpdateData(final ModelImage kImage, boolean reload) { - m_kImage = kImage; - if ( m_kVolume == null ) { - m_kPostfix = ""; - init(null, 0, false); - return; - } - initLUT(); - if ( reload ) - { - if ( m_kVolume[m_iTimeSlice] != null ) - { - m_kVolume[m_iTimeSlice].dispose(); - } - m_kVolumeTarget.Remove(); - m_kVolume[m_iTimeSlice] = initVolumeData(m_kImage, m_iTimeSlice, m_kVolumeTarget, m_kImage.getImageName(), true, false); - m_kVolumeTarget.SetImage(m_kVolume[m_iTimeSlice]); - } - else - { - m_kVolume[m_iTimeSlice] = resetVolumeData(m_kImage, m_iTimeSlice, m_kVolume[m_iTimeSlice], m_kVolumeTarget, m_kImage - .getImageName(), true, false); - } - InitScale(); - } - - - /** - * Changes the underlying image data and LUT. If the new image data is a different size than - * then previous one, recreate the volume image on the GPU, otherwise just overwrite it with - * the new data. - * @param kImage - * @param kLUT - * @param reload - */ - public void UpdateData(final ModelImage kImage, ModelLUT kLUT, boolean reload) { - if ( kLUT == null && !kImage.isColorImage()) - { - UpdateData(kImage, reload); - return; - } - m_kImage = kImage; - m_kLUT = kLUT; - if ( reload ) - { - if ( m_kVolume[m_iTimeSlice] != null ) - { - m_kVolume[m_iTimeSlice].dispose(); - } - m_kVolumeTarget.Remove(); - m_kVolume[m_iTimeSlice] = initVolumeData(m_kImage, m_iTimeSlice, m_kVolumeTarget, m_kImage.getImageName(), true, false); - m_kVolumeTarget.SetImage(m_kVolume[m_iTimeSlice]); - } - else - { - m_kVolume[m_iTimeSlice] = resetVolumeData(m_kImage, m_iTimeSlice, m_kVolume[m_iTimeSlice], m_kVolumeTarget, m_kImage - .getImageName(), true, false); - } - InitScale(); - } - - /** - * Update the LUT for the ModelImage. - * - * @param kLUT new LUT for ModelImage. - */ - public void UpdateImages(final ModelLUT kLUT) { - if (kLUT != null) { - VolumeImage.UpdateImages(m_kColorMapTarget, m_kColorMap, kLUT); - m_kLUT = kLUT; - } - } - - /** - * Update the LUT for the ModelImage. - * - * @param kLUT new LUT for ModelImage. - */ - public void UpdateImages(final ModelStorageBase kLUT) { - if ( (kLUT != null) && (kLUT instanceof ModelLUT)) { - VolumeImage.UpdateImages(m_kColorMapTarget, m_kColorMap, (ModelLUT)kLUT); - m_kLUT = (ModelLUT)kLUT; -// System.err.println("UpdateImages " + m_kColorMapTarget.GetName() + " " + kLUT. -// System.err.println(" " + m_kColorMapTarget.GetID() ); - - } - if ( (kLUT != null) && (kLUT instanceof ModelRGB)) { - ModelLUT.exportIndexedLUTMin((ModelRGB)kLUT, m_kColorMap.GetData()); - m_kColorMapTarget.Reload(true); - m_kRGBT = (ModelRGB)kLUT; - } - } - - /** - * Update the transfer function for the image iImage. - * - * @param kTransfer the new opacity transfer function - * @param iImage the image to modify (0 = volume image, 2 = gradient mag) - * @param kImage GradientMagitude image. - * @return boolean true when updated, false otherwise. - */ - public boolean UpdateImages(final TransferFunction kTransfer, final int iImage, final ModelImage kImage) { - if (iImage == 0) { - return UpdateImages2(m_kImage, m_kColorMapTarget, m_kColorMap, kTransfer); - } else if ( (iImage == 2) && (kImage != null) && (m_kOpacityMapTarget_GM != null) && (m_kOpacityMap_GM != null)) { - return UpdateImages(kImage, m_kOpacityMapTarget_GM, m_kOpacityMap_GM, kTransfer); - } - return false; - } - - /** - * In order to map line integrals of image intensity to RGB colors where each color channel is 8 bits, it is - * necessary to make sure that the integrals are in [0,255]. Producing a theoretical maximum value of a line - * integral is not tractable in an application. This method constructs an approximate maximum by integrating along - * each line of voxels in the image with line directions parallel to the coordinate axes. The 'processRay' call - * adjusts the line integrals using the estimate, but still clamps the integrals to 255 since the estimate might not - * be the true maximum. - * - * @return float Integral normalization factor. - */ - protected float computeIntegralNormalizationFactor() { - final int iXBound = m_kImage.getExtents()[0]; - final int iYBound = m_kImage.getExtents()[1]; - final int iZBound = m_kImage.getExtents()[2]; - - byte[] aucData = null; - int iSize = iXBound * iYBound * iZBound; - if (m_kImage.isColorImage()) { - iSize *= 4; - } - - aucData = new byte[iSize]; - - try { - m_kImage.exportDataUseMask(0, iSize, false, aucData); - } catch (final IOException e) { - e.printStackTrace(); - } - - // compute image normalization factor - int iX, iY, iZ, iBase, iSteps; - float fMaxIntegral = 0.0f; - float fTStep, fIntegral; - - // fix y and z, integrate over x - for (iY = 0; iY < iYBound; iY++) { - - for (iZ = 0; iZ < iZBound; iZ++) { - iBase = iXBound * (iY + (iYBound * iZ)); - iSteps = iXBound - 1; - fIntegral = 0.5f * ( (aucData[iBase] & 0x0ff) + (aucData[iBase + iSteps] & 0x0ff)); - fTStep = 1.0f / iSteps; - - for (iX = 1; iX < iSteps; iX++) { - fIntegral += (aucData[iBase + iX] & 0x0ff); - } - - fIntegral *= fTStep; - - if (fIntegral > fMaxIntegral) { - fMaxIntegral = fIntegral; - } - } - } - final int iXYProduct = iXBound * iYBound; - // fix x and z, integrate over y - for (iX = 0; iX < iXBound; iX++) { - - for (iZ = 0; iZ < iZBound; iZ++) { - iBase = iX + (iXYProduct * iZ); - iSteps = iYBound - 1; - fIntegral = 0.5f * ( (aucData[iBase] & 0x0ff) + (aucData[iBase + (iXBound * iSteps)] & 0x0ff)); - fTStep = 1.0f / iSteps; - - for (iY = 1; iY < iSteps; iY++) { - fIntegral += (aucData[iBase + (iXBound * iY)] & 0x0ff); - } - - fIntegral *= fTStep; - - if (fIntegral > fMaxIntegral) { - fMaxIntegral = fIntegral; - } - } - } - - // fix x and y, integrate over z - for (iX = 0; iX < iXBound; iX++) { - - for (iY = 0; iY < iYBound; iY++) { - iBase = iX + (iXBound * iY); - iSteps = iZBound - 1; - fIntegral = 0.5f * ( (aucData[iBase] & 0x0ff) + (aucData[iBase + (iXYProduct * iSteps)] & 0x0ff)); - fTStep = 1.0f / iSteps; - - for (iZ = 1; iZ < iSteps; iZ++) { - fIntegral += (aucData[iBase + (iXYProduct * iZ)] & 0x0ff); - } - - fIntegral *= fTStep; - - if (fIntegral > fMaxIntegral) { - fMaxIntegral = fIntegral; - } - } - } - aucData = null; - return (fMaxIntegral > 0.0f) ? (1.0f / fMaxIntegral) : 0.00f; - } - - /** - * Checks that the two input images match extents, units of measure and resolutions. The images - * may had different sizes (3D or 4D) the first 3-dimensions must match. - * @param kImage1 - * @param kImage2 - * @return true if the images match extends, units and resolutions. - */ - public static boolean checkImage(ModelImage kImage1, ModelImage kImage2 ) - { - for ( int i = 0; i < Math.min( kImage1.getExtents().length, kImage2.getExtents().length ); i++ ) - { - if ( kImage1.getExtents()[i] != kImage2.getExtents()[i] ) - { - return false; - } - if ( kImage1.getUnitsOfMeasure()[i] != kImage2.getUnitsOfMeasure()[i] ) - { - return false; - } - if ( kImage1.getResolutions(0)[i] != kImage2.getResolutions(0)[i] ) - { - return false; - } - } - return true; - } - - /** - * Generate 2D histogram from the input image and the gradient-magnitude - * - * @param kImage input GraphicsImage containing the ModelImage data - * @param kImageGM input GraphcisImage containing the Gradient Magnitude data. - * @param kPostFix post-fix for the image name. - */ - private void GenerateHistogram(final GraphicsImage[] kImage, final GraphicsImage[] kImageGM, final String kPostFix, - int start, int end ) - { - int iTMinX = 255, iTMaxX = 0; - int iTMinY = 255, iTMaxY = 0; - float max = Float.MIN_VALUE; - float min = Float.MAX_VALUE; - m_kHisto = new GraphicsImage[m_iTimeSteps]; - for (int t = start; t < end; t++) { - float[] afCount = new float[256 * 256]; - for (int i = 0; i < 256 * 256; i++) { - afCount[i] = 0; - } - - int a1; - int a2; - final byte[] abHistoData = kImageGM[t].GetData(); - final byte[] abData = kImage[t].GetData(); - if (m_kImage.isColorImage()) { - int iHisto = 0; - for (int i = 0; i < abData.length; i += 4) { - int iR = (abData[i]); - int iG = (abData[i + 1]); - int iB = (abData[i + 2]); - //a1 = (iR * 0.299 + iG * 0.587 + iB * 0.114); - a1 = (iR + iG + iB)/3; - a1 = (a1 & 0x00ff); - - iR = (abHistoData[i]); - iG = (abHistoData[i + 1]); - iB = (abHistoData[i + 2]); - //a2 = (short) (iR * 0.299 + iG * 0.587 + iB * 0.114); - a2 = (iR + iG + iB)/3; - a2 = (a2 & 0x00ff); - afCount[a1 + a2 * 256] += 1; - iHisto++; - } - } - else { - int iHisto = 0; - for (int i = 0; i < abData.length; i += 4) { - a1 = abData[i]; - a1 = (a1 & 0x00ff); - a2 = (abHistoData[iHisto]); - a2 = (a2 & 0x00ff); - afCount[a1 + a2 * 256] += 1; - iHisto += 4; - } - } - max = Float.MIN_VALUE; - min = Float.MAX_VALUE; - for (int i = 0; i < 256 * 256; ++i) { - afCount[i] = (float) Math.log(afCount[i]+1); - max = Math.max(afCount[i], max); - min = Math.min(afCount[i], min); - } - //System.err.println( min + " " + max ); - final byte[] abHisto = new byte[256 * 256]; - //int maxB = Integer.MIN_VALUE; - //int minB = Integer.MAX_VALUE; - for (int i = 0; i < 256 * 256; ++i) { - abHisto[i] = new Float((afCount[i] / max) * 255f).byteValue(); - //maxB = ( iVal > maxB ) ? iVal : maxB; - //minB = ( iVal < minB ) ? iVal : minB; - } - afCount = null; - - int iMinX = 255, iMaxX = 0; - int iIndex = 0; - for (int i = 0; i < 256; i++) { - for (int j = 0; j < 256; j++) { - iIndex = i * 256 + j; - if (abHisto[iIndex] > 50) { - if (iMinX > j) { - iMinX = j; - } - if (j > iMaxX) { - iMaxX = j; - } - } - } - } - - int iMinY = 255, iMaxY = 0; - for (int j = 0; j < 256; j++) { - for (int i = 0; i < 256; i++) { - iIndex = i * 256 + j; - if (abHisto[iIndex] > 50) { - if (iMinY > i) { - iMinY = i; - } - if (i > iMaxY) { - iMaxY = i; - } - } - } - } - if (iTMinX > iMinX) { - iTMinX = iMinX; - } - if (iTMaxX < iMaxX) { - iTMaxX = iMaxX; - } - - if (iTMinY > iMinY) { - iTMinY = iMinY; - } - if (iTMaxY < iMaxY) { - iTMaxY = iMaxY; - } - - // iMinX = 0; iMaxX = 255; - // iMinY = 0; iMaxY = 255; - - m_kHisto[t] = new GraphicsImage(GraphicsImage.FormatMode.IT_L8, 256, 256, (byte[]) null, new String( - "VolumeImageHisto" + kPostFix)); - m_kHisto[t].SetData(abHisto, 256, 256); - /* - ModelImage kTestHisto2D = new ModelImage( ModelStorageBase.UBYTE, new int[]{256,256}, "Histo2D" ); - try { - kTestHisto2D.importData(abHisto); - } catch (IOException e) { - e.printStackTrace(); - } - kTestHisto2D.calcMinMax(); - new ViewJFrameImage( kTestHisto2D ); */ - } - - m_kHistoTarget = new Texture(); - m_kHistoTarget.SetImage(m_kHisto[0]); - m_kHistoTarget.SetShared(true); - m_kHistoTarget.SetFilterType(Texture.FilterType.LINEAR); - m_kHistoTarget.SetWrapType(0, Texture.WrapType.CLAMP_BORDER); - m_kHistoTarget.SetWrapType(1, Texture.WrapType.CLAMP_BORDER); - m_kHistoTarget.SetWrapType(2, Texture.WrapType.CLAMP_BORDER); - - iTMinX = 0; - iTMaxX = Math.max( iTMaxX, iTMaxY ); - m_akHistoTCoord = new Vector2f[4]; - m_akHistoTCoord[0] = new Vector2f(iTMinX / 255.0f, iTMinX / 255.0f); - m_akHistoTCoord[1] = new Vector2f(iTMaxX / 255.0f, iTMinX / 255.0f); - m_akHistoTCoord[2] = new Vector2f(iTMaxX / 255.0f, iTMaxX / 255.0f); - m_akHistoTCoord[3] = new Vector2f(iTMinX / 255.0f, iTMaxX / 255.0f); - //m_akHistoTCoord[0] = new Vector2f(0f, 0f); - //m_akHistoTCoord[1] = new Vector2f(1f, 0f); - //m_akHistoTCoord[2] = new Vector2f(1f, 1f); - //m_akHistoTCoord[3] = new Vector2f(0f, 1f); - m_bHistoInit = true; - } - - /** - * Calculates and stores the gradient magnitude images (3D or 4D) for the input image. Or reads from disk. - * The data is stored in the GraphicsImage data structures and will be passed to the GPU to use in rendering. - * - * @param kImage input image - * @param kGradientMagnitude input Gradient Magnitude image, or null. - * @param bComputeLaplace when true the Laplace image is also calculated or read from dis. - private void GradientMagnitudeImage(final ModelImage kImage, ModelImage kGradientMagnitude, - boolean bComputeLaplace) { - - - if ( !m_bGMInit ) - { - ModelImage[] kImageGM = new ModelImage[m_iTimeSteps]; - String[] kImageName = new String[m_iTimeSteps]; - for (int i = 0; i < m_iTimeSteps; i++) { - kImageName[i] = ModelImage.makeImageName(kImage.getFileInfo(0).getFileName(), new String("_GM_" + i)); - - if ( kGradientMagnitude != null && checkImage(kImage, kGradientMagnitude )) - { - m_kVolumeGM[i] = VolumeImage.UpdateData(kGradientMagnitude, i, null, m_kVolumeGM[i], - m_kVolumeGMTarget, m_kVolumeGM[i].GetName(), true, false); - ModelImage.saveImage( kGradientMagnitude, kImageName[i] + ".xml", m_kDir ); - } - else - { - kImageGM[i] = ReadFromDisk(kImageName[i] + ".xml", m_kDir); - if ( kImageGM[i] != null && !checkImage(kImage, kImageGM[i] ) ) - { - kImageGM[i].disposeLocal(); - kImageGM[i] = null; - } - if (kImageGM[i] == null) { - JDialogGradientMagnitude kCalcMagnitude = new JDialogGradientMagnitude(null, m_akImages[i]); - kCalcMagnitude.setVisible(false); - kCalcMagnitude.setOutputNewImage(true); - kCalcMagnitude.setDisplayProgressBar(true); - kCalcMagnitude.setSeparateThread(false); - kCalcMagnitude.setSeparable(true); - kCalcMagnitude.setUseOCL(true); - kCalcMagnitude.actionPerformed(new ActionEvent(this, 0, "OK")); - kImageGM[i] = kCalcMagnitude.getResultImage(); - kCalcMagnitude = null; - } - if (kImageGM[i] == null) { - System.err.println("Gradient magnitude calculation returned null"); - m_kVolumeGM[i] = VolumeImage.UpdateData(kImage, i, null, m_kVolumeGM[i], m_kVolumeGMTarget, kImageName[i], true, false); - } else { - kImageGM[i].calcMinMax(); - m_akGradientMagMinMax[i] = new Vector2f( (float)kImageGM[i].getMin(), (float)kImageGM[i].getMax() ); - - if ( !( bComputeLaplace && !m_kImage.isColorImage() ) ) - { - m_kVolumeGM[i] = VolumeImage.UpdateData(kImageGM[i], 0, null, m_kVolumeGM[i], m_kVolumeGMTarget, kImageName[i], true, false); - } - } - } - } - - - if ( bComputeLaplace && !m_kImage.isColorImage() ) - { - for (int i = 0; i < m_iTimeSteps; i++) { - final String kImageNameL = ModelImage.makeImageName(kImage.getFileInfo(0).getFileName(), new String( - "_Laplacian_" + i)); - ModelImage kImageGMGM = null; - kImageGMGM = ReadFromDisk(kImageNameL + ".xml", m_kDir); - if ( kImageGMGM != null && !checkImage(kImage, kImageGMGM ) ) - { - kImageGMGM.disposeLocal(); - kImageGMGM = null; - } - if (kImageGMGM == null) { - final JDialogLaplacian kCalcLaplacian = new JDialogLaplacian(null, m_akImages[i]); - kCalcLaplacian.setVisible(false); - kCalcLaplacian.setOutputNewImage(true); - kCalcLaplacian.setDisplayProgressBar(true); - kCalcLaplacian.setSeparateThread(false); - kCalcLaplacian.setUseOCL(true); - kCalcLaplacian.setSeparable(true); - kCalcLaplacian.actionPerformed(new ActionEvent(this, 0, "OK")); - kImageGMGM = kCalcLaplacian.getResultImage(); - } - if (kImageGMGM != null) { - m_kVolumeGM[i] = createGM_Laplace(kImageGM[i], kImageGMGM, m_kVolumeGM[i], i, true); - } else { - m_kVolumeGM[i] = VolumeImage.UpdateData(kImageGM[i], 0, null, m_kVolumeGM[i], m_kVolumeGMTarget, kImageName[i], true, false); - } - final ViewJFrameImage kImageFrame = ViewUserInterface.getReference().getFrameContainingImage(kImageGMGM); - if (kImageFrame != null) { - kImageFrame.close(); - } else if (kImageGMGM != null) { - kImageGMGM.disposeLocal(); - kImageGMGM = null; - } - } - } - - m_bGMInit = true; - m_kVolumeGMTarget.SetImage(m_kVolumeGM[0]); - m_kVolumeGMTarget.Reload(true); - - for ( int i = 0; i < kImageGM.length; i++ ) - { - if (kImageGM[i] != null) { - - kImageGM[i].setImageDirectory( m_kDir ); - kImageGM[i].setImageName( kImageName[i] + ".xml" ); - ModelImage.saveImage(kImageGM[i], kImageName[i] + ".xml", m_kDir ); - - - final ViewJFrameImage kImageFrame = ViewUserInterface.getReference().getFrameContainingImage(kImageGM[i]); - if (kImageFrame != null) { - kImageFrame.close(); - } - - kImageGM[i].disposeLocal(); - kImageGM[i] = null; - } - } - } - } - */ - - public static ModelImage getGradientMagnitude( ModelImage kImage, int i ) - { - if ( kImage == null ) - { - return null; - } - int index = kImage.getExtents()[2] / 2; - float xRes = kImage.getFileInfo(index).getResolutions()[0]; - float zRes = kImage.getFileInfo(index).getResolutions()[2]; - - float correction = xRes / zRes; - float[] sigmas = new float[]{1f,1f,correction}; - - int dimX = kImage.getExtents().length > 0 ? kImage.getExtents()[0] : 1; - int dimY = kImage.getExtents().length > 1 ? kImage.getExtents()[1] : 1; - int dimZ = kImage.getExtents().length > 2 ? kImage.getExtents()[2] : 1; - ModelImage outputImage = new ModelImage( kImage.getDataType(), new int[]{dimX,dimY,dimZ}, "temp" ); - AlgorithmGradientMagnitudeSep gradientMagAlgo = new AlgorithmGradientMagnitudeSep( kImage, sigmas, true, false ); -// OpenCLAlgorithmGradientMagnitude gradientMagAlgo = new OpenCLAlgorithmGradientMagnitude(outputImage, kImage, sigmas, -// true, true, false); - gradientMagAlgo.setRed(true); - gradientMagAlgo.setGreen(true); - gradientMagAlgo.setBlue(true); - gradientMagAlgo.setRunningInSeparateThread(false); - gradientMagAlgo.run(); - float[] resultBuffer = gradientMagAlgo.getResultBuffer(); - try { - outputImage.importData(0, resultBuffer, true); - } catch (IOException e) {} -// gradientMagAlgo.gradientMagnitudeSep3D( i ); - gradientMagAlgo.finalize(); - gradientMagAlgo = null; - return outputImage; - } - - -// private ModelImage getLaplace( ModelImage kImage, int i ) -// { -// String kImageName = ModelImage.makeImageName(kImage.getFileInfo(0).getFileName(), new String("_Laplacian_" + i)); -// ModelImage kImageL = ReadFromDisk(kImageName + ".xml", m_kDir); -// if ( kImageL != null && !checkImage(kImage, kImageL ) ) -// { -// kImageL.disposeLocal(); -// kImageL = null; -// } -// if (kImageL == null) { -// JDialogLaplacian kCalcLaplacian = new JDialogLaplacian(null, m_akImages[i]); -// kCalcLaplacian.setVisible(false); -// kCalcLaplacian.setOutputNewImage(true); -// kCalcLaplacian.setDisplayProgressBar(true); -// kCalcLaplacian.setSeparateThread(false); -// kCalcLaplacian.setUseOCL(true); -// kCalcLaplacian.setSeparable(true); -// kCalcLaplacian.actionPerformed(new ActionEvent(this, 0, "OK")); -// kImageL = kCalcLaplacian.getResultImage(); -// kCalcLaplacian = null; -// -// kImageL.setImageDirectory( m_kDir ); -// kImageL.setImageName( kImageName + ".xml" ); -// JDialogBase.updateFileInfo( kImage, kImageL ); -// ModelImage.saveImage(kImageL, kImageName + ".xml", m_kDir ); -// final ViewJFrameImage kImageFrame = ViewUserInterface.getReference().getFrameContainingImage(kImageL); -// if (kImageFrame != null) { -// kImageFrame.setVisible(false); -// } -// } -// return kImageL; -// } - - /** - * Initialize the VolumeImage with the ModelImage data. - * @param kProgress progress bar - * @param iProgress progress bar increment - */ - private void init(final ViewJProgressBar kProgress, final int iProgress, boolean initGradientMagnitude) { - // Create LUTS for the ModelImage: - initLUT(); - // Initialize Texture Maps: - if ( !m_kImage.isColorImage() ) - { - initImages(); - } - else - { - initImagesColor(); - } - if ( initGradientMagnitude ) - { - SetGradientMagnitude(null, true, m_kPostfix); - } - if (kProgress != null) { - kProgress.updateValueImmed(kProgress.getValue() + iProgress); - } - } - - /** - * Intializes the Textures and GraphicsImages used to render the ModelImage this - * VolumeImage represents. - */ - private void initImages() { - m_fDRRNormalize = computeIntegralNormalizationFactor(); - // Initialize Color Map GraphicsImage: - m_kColorMap = initColorMap(); - // Initialize Opacity Map for the GradientMagnitude image: - m_kOpacityMap_GM = InitOpacityMap(m_kImage, new String(m_kPostfix + "_GM")); - - final int iXBound = m_kImage.getExtents()[0]; - final int iYBound = m_kImage.getExtents()[1]; - final int iZBound = m_kImage.getExtents()[2]; - - /* - * Map the ModelImage volume data to a texture image, including for the ModelImage gradient magnitude data: - */ - final int[] aiExtents = m_kImage.getExtents(); - final int iNDims = aiExtents.length; - String kImageName; - GraphicsImage.FormatMode type = GraphicsImage.FormatMode.IT_RGBA8888 ; - - - if (iNDims == 3) { // ModelImage is 3D: - m_iTimeSteps = 1; - } - else { // ModelImage is 4D: - m_iTimeSteps = aiExtents[3]; - } - // Allocate a 3D GraphicsImage for each 3D Volume - m_kVolume = new GraphicsImage[m_iTimeSteps]; - m_kVolumeGM = new GraphicsImage[m_iTimeSteps]; - m_akGradientMagMinMax = new Vector2f[m_iTimeSteps]; - - final int[] aiSubset = new int[] {aiExtents[0], aiExtents[1], aiExtents[2]}; - - for (int i = 0; i < m_iTimeSteps; i++) { -// System.err.println( "initiImages : " + i ); - if ( m_iTimeSteps > 1 ) - { - // Will generate the ModelImage GraphicsImage representation and separate the 4D ModelImage into - // the 3D Subset image. - m_kVolume[i] = initVolumeData(m_kImage, i, m_kVolumeTarget, m_kImage.getImageName(), true, false); - } - else - { - // Already 3D, just generate the GraphicsImage: - m_kVolume[0] = initVolumeData(m_kImage, m_iTimeSlice, m_kVolumeTarget, m_kImage.getImageName(), true, false); - } - - // Allocate GraphcisImage for Gradient Magnitude Texture: - kImageName = ModelImage.makeImageName(m_kImage.getFileInfo(0).getFileName(), - new String("_GM_" + i)); - m_kVolumeGM[i] = new GraphicsImage(type, iXBound, iYBound, iZBound, - (byte[])null, kImageName); - - } - // Initialize the Gradient Magnitude Texture and set its GraphicsImage: - m_kVolumeGMTarget = new Texture(); - m_kVolumeGMTarget.SetImage(m_kVolumeGM[0]); - m_kVolumeGMTarget.SetShared(true); - m_kVolumeGMTarget.SetFilterType(Texture.FilterType.LINEAR); - m_kVolumeGMTarget.SetWrapType(0, Texture.WrapType.CLAMP_BORDER); - m_kVolumeGMTarget.SetWrapType(1, Texture.WrapType.CLAMP_BORDER); - m_kVolumeGMTarget.SetWrapType(2, Texture.WrapType.CLAMP_BORDER); - - // Initialize the ModelImage Texture and set its GraphicsImage: - m_kVolumeTarget = new Texture(); - m_kVolumeTarget.SetImage(m_kVolume[0]); - m_kVolumeTarget.SetShared(true); - m_kVolumeTarget.SetFilterType(Texture.FilterType.LINEAR); - m_kVolumeTarget.SetWrapType(0, Texture.WrapType.CLAMP_BORDER); - m_kVolumeTarget.SetWrapType(1, Texture.WrapType.CLAMP_BORDER); - m_kVolumeTarget.SetWrapType(2, Texture.WrapType.CLAMP_BORDER); - - // Initialize the ColorMap Texture and set its GraphicsImage: - m_kColorMapTarget = new Texture(); - m_kColorMapTarget.SetImage(m_kColorMap); - m_kColorMapTarget.SetShared(true); - - // Initialize the Normal Map Texture and set its GraphicsImage: - m_kScratchTarget = new Texture(); - m_kScratchTarget.SetImage(new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, - (byte[])null, "ScratchBuffer")); - m_kScratchTarget.SetShared(true); - m_kScratchTarget.SetFilterType(Texture.FilterType.LINEAR); - m_kScratchTarget.SetWrapType(0, Texture.WrapType.CLAMP_BORDER); - m_kScratchTarget.SetWrapType(1, Texture.WrapType.CLAMP_BORDER); - m_kScratchTarget.SetWrapType(2, Texture.WrapType.CLAMP_BORDER); - - // Initialize the Opacity Map for the Gradient Magnitude Texture and set its GraphicsImage: - m_kOpacityMapTarget_GM = new Texture(); - m_kOpacityMapTarget_GM.SetImage(m_kOpacityMap_GM); - m_kOpacityMapTarget_GM.SetShared(true); - - // Initialize the Surface Mask Texture and set its GraphicsImage: - m_kSurfaceImage = new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, - new byte[4* iXBound * iYBound * iZBound], "SurfaceImage"); - m_kSurfaceTarget = new Texture(); - m_kSurfaceTarget.SetImage(m_kSurfaceImage); - m_kSurfaceTarget.SetShared(true); - m_kSurfaceTarget.SetFilterType(Texture.FilterType.LINEAR); - m_kSurfaceTarget.SetWrapType(0, Texture.WrapType.CLAMP_BORDER); - m_kSurfaceTarget.SetWrapType(1, Texture.WrapType.CLAMP_BORDER); - m_kSurfaceTarget.SetWrapType(2, Texture.WrapType.CLAMP_BORDER); - - // Calculate the scale factors for rendering the volume with a unit cube: - InitScale(); - } - - - private void initImagesColor() { - m_fDRRNormalize = computeIntegralNormalizationFactor(); - // Initialize Color Map GraphicsImage: - m_kColorMap = initColorMap(); - // Initialize Opacity Map for the GradientMagnitude image: - m_kOpacityMap_GM = InitOpacityMap(m_kImage, new String(m_kPostfix + "_GM")); - - final int iXBound = m_kImage.getExtents()[0]; - final int iYBound = m_kImage.getExtents()[1]; - final int iZBound = m_kImage.getExtents()[2]; - - /* - * Map the ModelImage volume data to a texture image, including for the ModelImage gradient magnitude data: - */ - final int[] aiExtents = m_kImage.getExtents(); - final int iNDims = aiExtents.length; - String kImageName; - GraphicsImage.FormatMode type = GraphicsImage.FormatMode.IT_RGBA8888 ; - - - if (iNDims == 3) { // ModelImage is 3D: - m_iTimeSteps = 1; - } - else { // ModelImage is 4D: - m_iTimeSteps = aiExtents[3]; - } - // A 4D ModelImage is separated into the 3D Volumes: -// m_akImages = new ModelImage[m_iTimeSteps]; - // Allocate a 3D GraphicsImage for each 3D Volume - m_kVolume = new GraphicsImage[m_iTimeSteps]; - m_kVolumeGM = new GraphicsImage[m_iTimeSteps]; - m_kNormal = new GraphicsImage[m_iTimeSteps]; - m_akGradientMagMinMax = new Vector2f[m_iTimeSteps]; - - final int[] aiSubset = new int[] {aiExtents[0], aiExtents[1], aiExtents[2]}; - for (int i = 0; i < m_iTimeSteps; i++) { - - if ( m_iTimeSteps > 1 ) - { - // Will generate the ModelImage GraphicsImage representation and separate the 4D ModelImage into - // the 3D Subset image. -// m_akImages[i] = new ModelImage(m_kImage.getType(), aiSubset, JDialogBase.makeImageName(m_kImage -// .getImageName(), "_" + i)); -// JDialogBase.updateFileInfo( m_kImage, m_akImages[i] ); - m_kVolume[i] = initVolumeData(m_kImage, i, m_kVolumeTarget, m_kImage.getImageName(), true, false); -// m_akImages[i].copyFileTypeInfo(m_kImage); -// m_akImages[i].calcMinMax(); - } - else - { - // Already 3D, just generate the GraphicsImage: -// m_akImages[0] = m_kImage; - m_kVolume[0] = initVolumeData(m_kImage, m_iTimeSlice, m_kVolumeTarget, m_kImage.getImageName(), true, false); - } - // Allocate GraphcisImage for Normal Map Texture: - kImageName = ModelImage.makeImageName(m_kImage.getFileInfo(0).getFileName(), - new String("_Normal_" + i)); - m_kNormal[i] = new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, - (byte[])null, kImageName); - - // Allocate GraphcisImage for Gradient Magnitude Texture: - kImageName = ModelImage.makeImageName(m_kImage.getFileInfo(0).getFileName(), - new String("_GM_" + i)); - m_kVolumeGM[i] = new GraphicsImage(type, iXBound, iYBound, iZBound, - (byte[])null, kImageName); - } - // Initialize the Gradient Magnitude Texture and set its GraphicsImage: - m_kVolumeGMTarget = new Texture(); - m_kVolumeGMTarget.SetImage(m_kVolumeGM[0]); - m_kVolumeGMTarget.SetShared(true); - m_kVolumeGMTarget.SetFilterType(Texture.FilterType.LINEAR); - m_kVolumeGMTarget.SetWrapType(0, Texture.WrapType.CLAMP_BORDER); - m_kVolumeGMTarget.SetWrapType(1, Texture.WrapType.CLAMP_BORDER); - m_kVolumeGMTarget.SetWrapType(2, Texture.WrapType.CLAMP_BORDER); - - // Initialize the ModelImage Texture and set its GraphicsImage: - m_kVolumeTarget = new Texture(); - m_kVolumeTarget.SetImage(m_kVolume[0]); - m_kVolumeTarget.SetShared(true); - m_kVolumeTarget.SetFilterType(Texture.FilterType.LINEAR); - m_kVolumeTarget.SetWrapType(0, Texture.WrapType.CLAMP_BORDER); - m_kVolumeTarget.SetWrapType(1, Texture.WrapType.CLAMP_BORDER); - m_kVolumeTarget.SetWrapType(2, Texture.WrapType.CLAMP_BORDER); - - // Initialize the ColorMap Texture and set its GraphicsImage: - m_kColorMapTarget = new Texture(); - m_kColorMapTarget.SetImage(m_kColorMap); - m_kColorMapTarget.SetShared(true); - - // Initialize the Normal Map Texture and set its GraphicsImage: - m_kNormalMapTarget = new Texture(); - m_kNormalMapTarget.SetImage(m_kNormal[0]); - m_kNormalMapTarget.SetShared(true); - m_kNormalMapTarget.SetFilterType(Texture.FilterType.LINEAR); - m_kNormalMapTarget.SetWrapType(0, Texture.WrapType.CLAMP_BORDER); - m_kNormalMapTarget.SetWrapType(1, Texture.WrapType.CLAMP_BORDER); - m_kNormalMapTarget.SetWrapType(2, Texture.WrapType.CLAMP_BORDER); - - - // Initialize the Normal Map Texture and set its GraphicsImage: - m_kScratchTarget = new Texture(); - m_kScratchTarget.SetImage(new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, - (byte[])null, "ScratchBuffer")); - m_kScratchTarget.SetShared(true); - m_kScratchTarget.SetFilterType(Texture.FilterType.LINEAR); - m_kScratchTarget.SetWrapType(0, Texture.WrapType.CLAMP_BORDER); - m_kScratchTarget.SetWrapType(1, Texture.WrapType.CLAMP_BORDER); - m_kScratchTarget.SetWrapType(2, Texture.WrapType.CLAMP_BORDER); - - // Initialize the Opacity Map for the Gradient Magnitude Texture and set its GraphicsImage: - m_kOpacityMapTarget_GM = new Texture(); - m_kOpacityMapTarget_GM.SetImage(m_kOpacityMap_GM); - m_kOpacityMapTarget_GM.SetShared(true); - - // Initialize the Surface Mask Texture and set its GraphicsImage: - m_kSurfaceImage = new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, - new byte[4* iXBound * iYBound * iZBound], "SurfaceImage"); - m_kSurfaceTarget = new Texture(); - m_kSurfaceTarget.SetImage(m_kSurfaceImage); - m_kSurfaceTarget.SetShared(true); - m_kSurfaceTarget.SetFilterType(Texture.FilterType.LINEAR); - m_kSurfaceTarget.SetWrapType(0, Texture.WrapType.CLAMP_BORDER); - m_kSurfaceTarget.SetWrapType(1, Texture.WrapType.CLAMP_BORDER); - m_kSurfaceTarget.SetWrapType(2, Texture.WrapType.CLAMP_BORDER); - - // Calculate the scale factors for rendering the volume with a unit cube: - InitScale(); - } - - /** - * Create a new LUT for the input image. - * - * @param kImage ModelImage. - */ - private void initLUT() { - - if (m_kImage.isColorImage()) { - final float[] x = new float[4]; - final float[] y = new float[4]; - final Dimension dim = new Dimension(256, 256); - - // Set ModelRGB min max values; - x[0] = 0; - y[0] = dim.height - 1; - - x[1] = 255 * 0.333f; - y[1] = (dim.height - 1) - ( (dim.height - 1) / 3.0f); - - x[2] = 255 * 0.667f; - y[2] = (dim.height - 1) - ( (dim.height - 1) * 0.67f); - - x[3] = 255; - y[3] = 0; - - final int[] RGBExtents = new int[2]; - RGBExtents[0] = 4; - RGBExtents[1] = 256; - m_kRGBT = new ModelRGB(RGBExtents); - m_kRGBT.getRedFunction().importArrays(x, y, 4); - m_kRGBT.getGreenFunction().importArrays(x, y, 4); - m_kRGBT.getBlueFunction().importArrays(x, y, 4); - m_kRGBT.makeRGB( -1); - } else { - final int[] dimExtentsLUT = new int[2]; - - dimExtentsLUT[0] = 4; - dimExtentsLUT[1] = 256; - - m_kLUT = new ModelLUT(ModelLUT.GRAY, 256, dimExtentsLUT); - - float min, max; - - if (m_kImage.getType() == ModelStorageBase.UBYTE) { - min = 0; - max = 255; - } else if (m_kImage.getType() == ModelStorageBase.BYTE) { - min = -128; - max = 127; - } else { - min = (float) m_kImage.getMin(); - max = (float) m_kImage.getMax(); - } - - final float imgMin = (float) m_kImage.getMin(); - final float imgMax = (float) m_kImage.getMax(); - - m_kLUT.resetTransferLine(min, imgMin, max, imgMax); - } - } - - /** - * Initialize the scale factors. Based on the ModelImage Volume. - */ - private void InitScale() { - - int dimX = m_kImage.getExtents().length > 0 ? m_kImage.getExtents()[0] : 1; - int dimY = m_kImage.getExtents().length > 1 ? m_kImage.getExtents()[1] : 1; - int dimZ = m_kImage.getExtents().length > 2 ? m_kImage.getExtents()[2] : 1; - m_iMaxExtent = Math.max( dimX, Math.max( dimY, dimZ ) ); - - final float fMaxX = (m_kImage.getExtents()[0] - 1) * m_kImage.getFileInfo(0).getResolutions()[0]; - final float fMaxY = (m_kImage.getExtents()[1] - 1) * m_kImage.getFileInfo(0).getResolutions()[1]; - final float fMaxZ = (m_kImage.getExtents()[2] - 1) * m_kImage.getFileInfo(0).getResolutions()[2]; - - m_fMax = fMaxX; - if (fMaxY > m_fMax) { - m_fMax = fMaxY; - } - if (fMaxZ > m_fMax) { - m_fMax = fMaxZ; - } - m_fX = fMaxX / m_fMax; - m_fY = fMaxY / m_fMax; - m_fZ = fMaxZ / m_fMax; - } - - /** - * Reads an image from disk. - * - * @param kImageName image name - * @param kDir directory - * @return ModelImage - */ - private static ModelImage ReadFromDisk(final String kImageName, final String kDir) { - - final File kFile = new File(kDir, kImageName); - if ( !kFile.exists()) { - return null; - } - - final FileIO fileIO = new FileIO(); - return fileIO.readImage( kImageName, kDir ); - //return fileIO.readXML(kImageName + ".xml", kDir, false, false); - } - - private void readObject(final java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { - m_kDir = (String) in.readObject(); - if ( !m_kDir.equals("null")) { - final String kImageName = (String) in.readObject(); - m_kPostfix = (String) in.readObject(); - m_kImage = ReadFromDisk(kImageName, m_kDir); - init(null, 0, true); - } - } - - - /** - * Go to the next 3D volume sub-image for the 4D animation. - * Updates the Textures and causes them to be reloaded onto the GPU. - */ - private void update4D() { - if ( m_kImage == null ) return; - - m_kVolumeTarget.SetImage(m_kVolume[m_iTimeSlice]); - m_kVolumeTarget.Reload(true); - if ( m_bGMInit ) - { - if ( m_kVolumeGM[m_iTimeSlice] == null ) - { - ModelImage gmImage = getGradientMagnitude( m_kImage, m_iTimeSlice ); - m_akGradientMagMinMax[m_iTimeSlice] = new Vector2f( (float)gmImage.getMin(), (float)gmImage.getMax() ); - gmImage.disposeLocal(); - m_kVolumeGMTarget.SetImage(createGM_Laplace(gmImage, null, null, 0, true)); - m_kVolumeGMTarget.Reload(true); - } - else - { - m_kVolumeGMTarget.SetImage(m_kVolumeGM[m_iTimeSlice]); - m_kVolumeGMTarget.Reload(true); - } - } - if ( m_bNormalsInit && m_kImage.isColorImage() ) - { - m_kNormalMapTarget.SetImage(m_kNormal[m_iTimeSlice]); - m_kNormalMapTarget.Reload(true); - } - if ( m_bHistoInit && (m_kHisto[m_iTimeSlice] != null )) - { - m_kHistoTarget.SetImage(m_kHisto[m_iTimeSlice]); - m_kHistoTarget.Reload(true); - } - - m_kImage.setTimeSlice(m_iTimeSlice); - } - - /** - * Called when the opacity transfer function changes. This function updates the Texture - * and causes the data to be reloaded onto the GPU. - * - * @param kImage the ModelImage the transfer function applies to. - * @param kOpacityTexture the opacity Texture passed to the GPU - * @param kOpacityMap the opacity data stored in the GraphicsImage - * @param kTransfer the new transfer function. - */ - private boolean UpdateImages(final ModelImage kImage, final Texture kOpacityTexture, - final GraphicsImage kOpacityMap, final TransferFunction kTransfer) { - final int iLutHeight = 256; - final float[] afData = kOpacityMap.GetFloatData(); - - final float fRange = (float) (kImage.getMax() - kImage.getMin()); - final float fStep = fRange / iLutHeight; - float fDataValue = (float) kImage.getMin(); - for (int i = 0; i < iLutHeight; i++) { - afData[i] = (kTransfer.getRemappedValue(fDataValue, iLutHeight) / 255.0f); - fDataValue += fStep; - } - kOpacityTexture.Reload(true); - return true; - } - - public TransferFunction getOpacityFn() { - return opacityTransferFn; - } - - /** - * Update the opacity transfer function. - * - * @param kImage the ModelImage the transfer function applies to. - * @param kOpacityTexture the opacity Texture passed to the GPU - * @param kOpacityMap the opacity data stored in the GraphicsImage - * @param kTransfer the new transfer function. - */ - private boolean UpdateImages2(final ModelImage kImage, final Texture kOpacityTexture, - final GraphicsImage kOpacityMap, final TransferFunction kTransfer) { - opacityTransferFn = new TransferFunction(kTransfer); - final int iLutHeight = kOpacityMap.GetBound(0); - final byte[] abData = kOpacityMap.GetData(); - - final float fRange = (float) (kImage.getMax() - kImage.getMin()); - final float fStep = fRange / iLutHeight; - float fDataValue = (float) kImage.getMin(); - float fVal; - for (int i = 0; i < iLutHeight; i++) { - fVal = (kTransfer.getRemappedValue(fDataValue, iLutHeight) / 255.0f); - abData[i * 4 + 3] = (byte) (fVal * 255); - fDataValue += fStep; - } - kOpacityTexture.Reload(true); - return true; - } - - private void writeObject(final java.io.ObjectOutputStream out) throws IOException { - if (m_kImage != null) { - out.writeObject(m_kDir); - out.writeObject(m_kImage.getImageFileName()); - out.writeObject(m_kPostfix); - m_kImage.saveImage(m_kDir, m_kImage.getImageFileName(), FileUtility.XML, false, false); - } else { - out.writeObject("null"); - } - } -} +package gov.nih.mipav.view.renderer.WildMagic.Render; + + +import static java.lang.System.nanoTime; +import static java.lang.System.out; +import gov.nih.mipav.model.algorithms.filters.AlgorithmGradientMagnitudeSep; +import gov.nih.mipav.model.algorithms.filters.OpenCL.filters.OpenCLAlgorithmGradientMagnitude; +import gov.nih.mipav.model.algorithms.filters.OpenCL.filters.OpenCLAlgorithmVolumeNormals; +import gov.nih.mipav.model.file.*; +import gov.nih.mipav.model.structures.*; + +import gov.nih.mipav.view.*; +import gov.nih.mipav.view.dialogs.*; +import gov.nih.mipav.view.renderer.WildMagic.VolumeTriPlanarInterface; + +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.io.*; +import java.nio.Buffer; +import java.util.BitSet; +import java.util.Vector; + + +import org.jocl.CL; + +import WildMagic.LibFoundation.Mathematics.ColorRGB; +import WildMagic.LibFoundation.Mathematics.ColorRGBA; +import WildMagic.LibFoundation.Mathematics.Vector2f; +import WildMagic.LibGraphics.Rendering.*; + +/** * + * The VolumeImage class provides an interface between the MIPAV ModelImage and the 2D and 3D Textures used to render + * the ModelImage on the GPU. The VolumeImage creates the supporting Texture and GraphicImage objects that pass + * the ModelImage data to the GPU. It also creates Texture and GraphicsImage objects for the ModelImage Look-up Table (LUT) + * and the ModelImage opacity transfer function. Other textures that are used for advanced volume rendering, such as + * a normal map for surface rendering, and the gradient-magnitude and laplace images for the multi-histogram rendering are + * calculated and passed on-demand to the GPU when the user selects these options in the Volume-Renderer user-interface. + * + * The VolumeImage data structure handles all GPU content for rendering one ModelImage. All Textures and GraphicsImages + * are initialized and stored in the VolumeImage. When needed by the renderer they are loaded onto the GPU. Any images + * that are derived from the ModelImage: Normal map, Gradient Magnitude, Laplace, are either read from file or calculated + * and then written to file. The supporting files are stored in a directory on disk located next to the original ModelImage. + * The directory is named with the ModelImage name followed by "_RenderFiles". + * + * + */ +public class VolumeImage implements Serializable { + /** */ + private static final long serialVersionUID = -7254697711265907746L; + + /** Reference to ModelImage image */ + private ModelImage m_kImage; + private ModelImage m_kImageGM; + + /** GraphicsImage contains GM opacity transfer function data: */ + private GraphicsImage m_kOpacityMap_GM = null; + + /** + * Texture contains texture filter modes and GraphicsImage for opacity transfer function: + */ + private Texture m_kOpacityMapTarget_GM = null; + + /** Data storage for volume: */ + private GraphicsImage[] m_kVolume; + + /** Texture object for data: */ + private Texture m_kVolumeTarget; + + /** Data storage for normals: */ + private GraphicsImage[] m_kNormal; + /** Set to true if the Normal Map has been initialized. */ + private boolean m_bNormalsInit = false; + + /** Texture object for normal map: */ + private Texture m_kNormalMapTarget; + + /** Texture object for GPU computations: */ + private Texture m_kScratchTarget; + + /** Data storage for color map: */ + private GraphicsImage m_kColorMap; + + /** Texture object for color map: */ + private Texture m_kColorMapTarget; + + /** Data storage for volume gradient magnitude: */ + private GraphicsImage[] m_kVolumeGM; + /** Set to true if the Gradient Magnitude texture map has been initialized. */ + private boolean m_bGMInit = false; + + /** Texture object for volume gradient magnitude data: */ + private Texture m_kVolumeGMTarget; + + /** Data storage for surfaces: */ + private GraphicsImage m_kSurfaceImage; + + /** Texture object for surfaces: */ + private Texture m_kSurfaceTarget; + + /** ModelLUT */ + private ModelLUT m_kLUT = null; + + /** ModelRGB */ + private ModelRGB m_kRGBT = null; + + /** Image scale factors for display in 3D */ + private float m_fX = 1, m_fY = 1, m_fZ = 1, m_fMax = 1; + private int m_iMaxExtent = 1; + + /** Image name post-fix typically either 'A' or 'B' */ + private String m_kPostfix = null; + + /** Directory for calculated images */ + private String m_kDir = null; + + /** Histogram data for multi-histogram interface */ + private GraphicsImage[] m_kHisto = null; + /** Set to true when the multi-histogram histogram texture has been initialized. */ + private boolean m_bHistoInit = false; + + /** Texture object for data: */ + private Texture m_kHistoTarget; + + /** Texture coordinates for displaying histogram in 2D */ + private Vector2f[] m_akHistoTCoord = null; + + private float m_fDRRNormalize = 255.0f; + + /** Current position in time (4D data) */ + private int m_iTimeSlice = 0; + + /** Total number of time-slices (4D data) */ + private int m_iTimeSteps = 0; + + /** 3D sub-images (4D data) */ +// private ModelImage[] m_akImages; +// private ModelImage[] m_akImagesGM; + + private Vector2f[] m_akGradientMagMinMax; + + private TransferFunction opacityTransferFn; + + /* Default Constructor */ + public VolumeImage() {} + + /** + * Create a Volume image with the input ModelImage. The supporting images for advanced volume rendering, such as + * the normal map, gradient magnitude and laplace images are generated on-demand and stored in a directory for + * later use. The directory is created if it does not already exist, with the ModelImage name + "_RenderFiles" as + * the directory name. + * + * @param bClone, when true clone the input ModelImage, when false reference the ModelImage + * @param kImage input ModelImage + * @param kPostfix Postfix for images 'A' or 'B' + * @param kProgress progress bar + * @param iProgress progress bar increment + */ + public VolumeImage(boolean bClone, final ModelImage kImage, final String kPostfix, final ViewJProgressBar kProgress, final int iProgress) { + this( bClone, kImage, kPostfix, kProgress, iProgress, true ); + } + + public VolumeImage(boolean bClone, final ModelImage kImage, final String kPostfix, final ViewJProgressBar kProgress, final int iProgress, boolean initGradientMagnitude) { + m_kPostfix = new String(kPostfix); + // clone the input image, in the future this might be a reference. + if ( bClone ) + { + m_kImage = (ModelImage)kImage.clone(); + } + else + { + m_kImage = kImage; + } + // Initialize the Texture maps. + init(kProgress, iProgress, initGradientMagnitude); + } + + /** + * Copy the data from the input GraphicsImage and return a new ModelImage of that data. + * Any changes to the GraphicsImage that occur only on the GPU can first be written from + * the GPU back into the GraphicsImage CPU data storage. This enables calculations that + * are performed on the GPU to be written back into a ModelImage data structure. + * + * @param kImage Graphics Image to copy + * @param bSwap when true convert from RGBA (graphics format) to ARGB (ModelImage format) + * @return new ModelImage from Volume Texture on GPU. + */ + public static ModelImage CreateImageFromTexture(final GraphicsImage kImage, final boolean bSwap) { + final int iXBound = kImage.GetBound(0); + final int iYBound = kImage.GetBound(1); + final int iZBound = kImage.GetBound(2); + final int iSize = iXBound * iYBound * iZBound; + final int[] extents = new int[] {iXBound, iYBound, iZBound}; + + ModelImage kResult = null; + if (kImage.GetFormat() == GraphicsImage.FormatMode.IT_RGBA8888) { + byte[] aucData = kImage.GetData(); + if (bSwap) { + byte bVal = 0; + aucData = new byte[4 * iXBound * iYBound * iZBound]; + for (int i = 0; i < iSize; i += 4) { + if (kImage.GetData()[i + 1] > bVal) { + bVal = kImage.GetData()[i + 1]; + } + aucData[i] = kImage.GetData()[i + 3]; + aucData[i + 1] = kImage.GetData()[i + 1]; + aucData[i + 2] = kImage.GetData()[i + 2]; + aucData[i + 3] = kImage.GetData()[i]; + //System.err.println( kImage.GetData()[i + 3] + " " + kImage.GetData()[i + 1] + " " + kImage.GetData()[i + 2] ); + } + // System.err.println( bVal ); + } + try { + kResult = new ModelImage(ModelStorageBase.ARGB, extents, ""); + kResult.importData(0, aucData, true); + } catch (final IOException e) { + e.printStackTrace(); + } + } else { + final byte[] aiImageData = kImage.GetData(); + try { + kResult = new ModelImage(ModelStorageBase.UBYTE, extents, ""); + kResult.importData(0, aiImageData, true); + } catch (final IOException e) { + e.printStackTrace(); + } + } + return kResult; + } + + /** + * Initialize the textures for the color lookup table. + * + * @param kLUT the new LUT. + * @param kRGBT the new RGB table. + * @param kPostfix the string postfix to concatenate to the "ColorMap" image name. + * @return GraphicsImage, the new GraphicsImage storing the colormap lookup table. + */ + public static GraphicsImage InitColorMap( Texture kTexture, GraphicsImage kImage, final ModelStorageBase kLUT, final String kPostFix) { + byte[] aucData; + if ( kImage == null ) + { + aucData = new byte[256 * 4]; + } + else + { + aucData = kImage.GetData(); + } + if (kLUT instanceof ModelLUT ) { + // ModelImage is Color, initialize the ModelRGB + ModelLUT.exportIndexedLUTMin((ModelLUT)kLUT, aucData); + } + else if (kLUT instanceof ModelRGB ) { + // Initialize the ModelLUT + ModelLUT.exportIndexedLUTMin((ModelRGB)kLUT, aucData); + } + if ( kImage == null ) + { + // Return the new GraphicsImage containing the table data: + return new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, 256, aucData, new String("ColorMap" + kPostFix)); + } + if ( kTexture != null ) + { + kTexture.Reload(true); + } + return kImage; + } + + + private GraphicsImage initColorMap() { + final byte[] aucData = new byte[256 * 4]; + if (m_kRGBT != null) + { + // ModelImage is Color, initialize the ModelRGB + ModelLUT.exportIndexedLUTMin(m_kRGBT, aucData); + } else if ( m_kLUT != null ) + { + // Initialize the ModelLUT + ModelLUT.exportIndexedLUTMin(m_kLUT, aucData); + } + // Return the new GraphicsImage containing the table data: + return new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, 256, 1, aucData, new String("ColorMap" + m_kImage.getImageName() + m_kPostfix)); + } + + + /** + * When a ModelImage changes on the CPU, this function is used to update the ModelImage + * data on the CPU. + * + * @param kImage Modified ModelImage to copy into the GPU Texture and GraphicsImage + * @param iTimeSlice time value for 4D image, 0 otherwise + * @param kNewImage a new ModelImage (always 3D) that the data or data subset for 4D image can be copied into (when non-null). + * @param kVolumeImage GraphicsImage that will hold the ModelImage data + * @param kVolumeTexture Texture object containing the GraphicsImage + * @param kImageName new image name for the new ModelImage. + * @param bSwap when true swap the ARGB (ModelImage) color data representation to a RGBA (GPU) color representation. + * @return + */ + public static GraphicsImage UpdateData(final ModelImage kImage, final int iTimeSlice, final ModelImage kNewImage, + final GraphicsImage kVolumeImage, final Texture kVolumeTexture, final String kImageName, + final boolean bSwap, final boolean bRescale) { + GraphicsImage kReturn = kVolumeImage; + final int iXBound = kImage.getExtents()[0]; + final int iYBound = kImage.getExtents()[1]; + final int iZBound = kImage.getExtents()[2]; + + byte[] aucData = null; + int iSize = iXBound * iYBound * iZBound; + if (kImage.isColorImage()) { + iSize *= 4; + aucData = new byte[iSize]; + try { + kImage.exportDataUseMask(iTimeSlice * iSize, iSize, bRescale, aucData); + if (bSwap) { + for (int i = 0; i < iSize; i += 4) { + final byte tmp = aucData[i]; + aucData[i] = aucData[i + 1]; + aucData[i + 1] = aucData[i + 2]; + aucData[i + 2] = aucData[i + 3]; + aucData[i + 3] = tmp; + } + } + } catch (final IOException e) { + e.printStackTrace(); + } + if (kReturn == null) { + kReturn = new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, aucData, + kImageName); + } else { + kReturn.SetData(aucData, iXBound, iYBound, iZBound); + } + } else { + aucData = new byte[iSize]; + try { + kImage.exportDataUseMask(iTimeSlice * iSize, iSize, bRescale, aucData); + // Temporary make the texture an RGBA texture until JOGL2 fixes NPOT textures. + byte[] aucData2 = new byte[iSize*4]; + for (int i = 0; i < iSize; i++) { + aucData2[i * 4 + 0] = aucData[i]; + aucData2[i * 4 + 1] = aucData[i]; + aucData2[i * 4 + 2] = aucData[i]; + aucData2[i * 4 + 3] = 1; + } + + if (kReturn == null) { + kReturn = new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, aucData2, + kImageName); + } else { + kReturn.SetData(aucData2, iXBound, iYBound, iZBound); + } + } catch (final IOException e) { + e.printStackTrace(); + } + + } + if (kNewImage != null) { + try { + kNewImage.importData(0, aucData, true); + } catch (final IOException e) {} + } + if (kVolumeTexture != null) { + kVolumeTexture.Reload(true); + } + return kReturn; + } + + private GraphicsImage initVolumeData(final ModelImage kImage, final int iTimeSlice, + final Texture kVolumeTexture, final String kImageName, final boolean bSwap, final boolean bRescale) + { + GraphicsImage kReturn = null; + final int iXBound = kImage.getExtents()[0]; + final int iYBound = kImage.getExtents()[1]; + final int iZBound = kImage.getExtents()[2]; + + byte[] aucData = null; + int iSize = iXBound * iYBound * iZBound; + if (kImage.isColorImage()) { + iSize *= 4; + aucData = new byte[iSize]; + try { + kImage.exportDataUseMask(iTimeSlice * iSize, iSize, bRescale, aucData); + if (bSwap) { + for (int i = 0; i < iSize; i += 4) { + final byte tmp = aucData[i]; + aucData[i] = aucData[i + 1]; + aucData[i + 1] = aucData[i + 2]; + aucData[i + 2] = aucData[i + 3]; + aucData[i + 3] = tmp; + } + } + } catch (final IOException e) { + e.printStackTrace(); + } + kReturn = new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, aucData, + kImageName); + } else { + aucData = new byte[iSize]; + try { + kImage.exportDataUseMask(iTimeSlice * iSize, iSize, bRescale, aucData); + // Temporary make the texture an RGBA texture until JOGL2 fixes NPOT textures. + byte[] aucData2 = new byte[iSize*4]; + for (int i = 0; i < iSize; i++) { + aucData2[i * 4 + 0] = aucData[i]; + aucData2[i * 4 + 1] = aucData[i]; + aucData2[i * 4 + 2] = aucData[i]; + aucData2[i * 4 + 3] = 1; + } + + kReturn = new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, aucData2, + kImageName); + } catch (final IOException e) { + e.printStackTrace(); + } + + } + if (kVolumeTexture != null) { + kVolumeTexture.Reload(true); + } + return kReturn; + } + + private GraphicsImage resetVolumeData(final ModelImage kImage, final int iTimeSlice, GraphicsImage kGraphicsImage, + final Texture kVolumeTexture, final String kImageName, final boolean bSwap, final boolean bRescale) + { + final int iXBound = kImage.getExtents()[0]; + final int iYBound = kImage.getExtents()[1]; + final int iZBound = kImage.getExtents()[2]; + int iSize = iXBound * iYBound * iZBound; + + byte[] aucData = null; + if (kImage.isColorImage()) { + iSize *= 4; + aucData = kGraphicsImage.GetData(); + try { + kImage.exportDataUseMask(iTimeSlice * iSize, iSize, bRescale, aucData); + if (bSwap) { + for (int i = 0; i < iSize; i += 4) { + final byte tmp = aucData[i]; + aucData[i] = aucData[i + 1]; + aucData[i + 1] = aucData[i + 2]; + aucData[i + 2] = aucData[i + 3]; + aucData[i + 3] = tmp; + } + } + } catch (final IOException e) { + e.printStackTrace(); + } + } + else { + aucData = new byte[iSize]; + try { + kImage.exportDataUseMask(iTimeSlice * iSize, iSize, bRescale, aucData); + byte[] aucData2 = kGraphicsImage.GetData(); + for (int i = 0; i < iSize; i++) { + aucData2[i * 4 + 0] = aucData[i]; + aucData2[i * 4 + 1] = aucData[i]; + aucData2[i * 4 + 2] = aucData[i]; + aucData2[i * 4 + 3] = 1; + } + } catch (final IOException e) { + e.printStackTrace(); + } + + } + if (kVolumeTexture != null) { + kVolumeTexture.Reload(true); + } + return kGraphicsImage; + } + + private void addNormals(final ModelImage kImage, final int iTimeSlice) { + final int iXBound = kImage.getExtents()[0]; + final int iYBound = kImage.getExtents()[1]; + final int iZBound = kImage.getExtents()[2]; + + int iSize = iXBound * iYBound * iZBound; + byte[] aucData = new byte[iSize * 4]; + try { + kImage.exportDataUseMask(0, iSize * 4, true, aucData); + byte[] volumeData = m_kVolume[iTimeSlice].GetData(); + for (int i = 0; i < iSize; i++) { + volumeData[i*4 + 1] = aucData[i*4 + 1]; + volumeData[i*4 + 2] = aucData[i*4 + 2]; + volumeData[i*4 + 3] = aucData[i*4 + 3]; + } + } catch (final IOException e) { + e.printStackTrace(); + } + m_kVolumeTarget.Reload(true); + } + + + private GraphicsImage createGM_Laplace(final ModelImage kImageGM, final ModelImage kImageL, + final GraphicsImage kVolumeImage, + final int iTimeSlice, final boolean bSwap) { + + GraphicsImage kReturn = kVolumeImage; + final int iXBound = kImageGM.getExtents()[0]; + final int iYBound = kImageGM.getExtents()[1]; + final int iZBound = kImageGM.getExtents()[2]; + + int iSize = iXBound * iYBound * iZBound; + byte[] aucDataL = new byte[iSize]; + + if ( kImageL != null ) + { + try { + kImageL.exportDataUseMask(0, iSize, false, aucDataL); + } catch (final IOException e) { + e.printStackTrace(); + } + } + + byte[] aucDataGM = null; + if (kImageGM.isColorImage()) { + iSize *= 4; + aucDataGM = new byte[iSize]; + try { + kImageGM.exportDataUseMask(0, iSize, false, aucDataGM); + if (bSwap) { + for (int i = 0, j = 0; i < iSize; i += 4) { + aucDataGM[i] = aucDataGM[i + 1]; + aucDataGM[i + 1] = aucDataGM[i + 2]; + aucDataGM[i + 2] = aucDataGM[i + 3]; + if ( kImageL != null ) + { + aucDataGM[i + 3] = aucDataL[j++]; + } + } + } + kImageGM.importData( 0, aucDataGM, false ); + } catch (final IOException e) { + e.printStackTrace(); + } + if (kReturn == null) { + kReturn = new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, aucDataGM, + kImageGM.getImageName()); + } else { + kReturn.SetData(aucDataGM, iXBound, iYBound, iZBound); + } + } + else + { + try { + aucDataGM = new byte[iSize]; + kImageGM.exportDataUseMask(0, iSize, false, aucDataGM); + byte[] aucData2 = new byte[iSize*4]; + for (int i = 0; i < iSize; i++) { + aucData2[i * 4 + 0] = aucDataGM[i]; + aucData2[i * 4 + 1] = aucDataGM[i]; + aucData2[i * 4 + 2] = aucDataGM[i]; + if ( kImageL != null ) + { + aucData2[i * 4 + 3] = aucDataL[i]; + } + } + + if (kReturn == null) { + kReturn = new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, aucData2, + kImageGM.getImageName()); + } else { + kReturn.SetData(aucData2, iXBound, iYBound, iZBound); + } + } catch (final IOException e) { + e.printStackTrace(); + } catch ( java.lang.OutOfMemoryError e ) { + return null; + } + aucDataGM = null; + } + return kReturn; + } + + + + /** + * Creates a new GraphicsImage for the input ModelSimpleImage. The ModelSimpleImage data is + * referenced by the new GraphicsImage and will be passed to the GPU as a texture. + * @param kImage input ModelSimpleImage. + * @param kImageName name for the GraphicsImage. + * @return a new GraphcisImage. + */ + public static GraphicsImage UpdateData(final ModelSimpleImage kImage, final String kImageName) { + final GraphicsImage.FormatMode eType = GraphicsImage.FormatMode.IT_L32F; + + if (kImage.nDims == 3) { + return new GraphicsImage(eType, kImage.extents[0], kImage.extents[1], kImage.extents[2], kImage.data, + kImageName); + } + return new GraphicsImage(eType, kImage.extents[0], kImage.extents[1], 1, kImage.data, kImageName); + } + + /** + * When the LUT changes, this function updates the LUT data on the GPU. + * + * @param kColorTexture the color-map Texture object. + * @param kColorMap the color-map GraphicsImage object (stores data). + * @param kLUT the updated or new LUT. + */ + public static void UpdateImages(final Texture kColorTexture, final GraphicsImage kColorMap, final ModelLUT kLUT) { + if (kLUT == null) { + return; + } + ModelLUT.exportIndexedLUTMin(kLUT, kColorMap.GetData()); + kColorTexture.Reload(true); + } + + /** + * When the ModelImage data is rendered as a solid surface, the Normal map is used in the rendering. + * The Normal map is calculated on the GPU by one of the GLSL shader programs. This function is called + * after the GPU calculation has finished and the GPU data has been copied into a new ModelImage on the CPU + * the new ModelImage then contains the Normal map information, which is written into a file and + * copied into the Normal map GraphicsImage used to render the original ModelImage. + * + * @param i current 3D sub-image for 4D data. If the data is 3D this value should be 0. + * @param kImage a new ModelImage containing the calculated Normals. + */ + public void CopyNormalFiles(final int i, final ModelImage kImage) { + final String kImageName = ModelImage.makeImageName(m_kImage.getFileInfo(0).getFileName(), "_Normal_" + + i); + JDialogBase.updateFileInfo( m_kImage, kImage ); + ModelImage.saveImage( kImage, kImageName + ".xml", m_kDir, false ); + if ( m_kImage.isColorImage() ) + { + m_kNormal[i] = initVolumeData(kImage, 0, m_kNormalMapTarget, kImage.getImageName(), true, true); + } + else + { + addNormals(kImage, i); + } + } + + /** + * Read the current Volume Texture from the GPU and return a new ModelImage of that data. + * + * @return new ModelImage from Volume Texture on GPU. + */ + public static ModelImage CreateImageFromTexture(final GraphicsImage kImage) { + final int[] extents = new int[ kImage.GetDimension() ]; + for ( int i = 0; i < extents.length; i++ ) + { + extents[i] = kImage.GetBound(i); + } + + //GraphicsImage.Type eType = kImage.GetType(); + int type = ModelStorageBase.ARGB; + /* + switch ( eType ) + { + case IT_BYTE : type = ModelStorageBase.BYTE; break; + case IT_UBYTE : type = ModelStorageBase.UBYTE; break; + case IT_SHORT : type = ModelStorageBase.SHORT; break; + case IT_USHORT : type = ModelStorageBase.USHORT; break; + case IT_INT : type = ModelStorageBase.INTEGER; break; + case IT_UINT : type = ModelStorageBase.UINTEGER; break; + case IT_LONG : type = ModelStorageBase.LONG; break; + case IT_FLOAT : type = ModelStorageBase.FLOAT; break; + case IT_DOUBLE : type = ModelStorageBase.DOUBLE; break; + } + */ + final ModelImage kResult = new ModelImage(type, extents, kImage.GetName() ); + int size = kImage.GetQuantity(); + for ( int i = 0; i < size;i++ ) + { + kResult.set(i*4 + 1, kImage.GetData()[i*3 + 0]); + kResult.set(i*4 + 2, kImage.GetData()[i*3 + 1]); + kResult.set(i*4 + 3, kImage.GetData()[i*3 + 2]); + } + return kResult; + } + + /** + * Memory cleanup. + */ + public void dispose() { + if (m_kImage == null) { + return; + } + m_kImage.disposeLocal(); + m_kImage = null; + + for (final GraphicsImage element : m_kVolume) { + element.dispose(); + } + m_kVolume = null; + m_kVolumeTarget.dispose(); + m_kVolumeTarget = null; + + if ( m_kNormal != null ) + { + for (final GraphicsImage element : m_kNormal) { + element.dispose(); + } + m_kNormal = null; + } + if ( m_kNormalMapTarget != null ) + { + m_kNormalMapTarget.dispose(); + m_kNormalMapTarget = null; + } + + m_kScratchTarget.dispose(); + m_kScratchTarget = null; + + m_kColorMap.dispose(); + m_kColorMap = null; + m_kColorMapTarget.dispose(); + m_kColorMapTarget = null; + + if ( m_kImageGM != null ) + { + m_kImageGM.disposeLocal(); + m_kImageGM = null; + } + + for (final GraphicsImage element : m_kVolumeGM) { + if ( element != null ) + element.dispose(); + } + m_kVolumeGM = null; + m_kVolumeGMTarget.dispose(); + m_kVolumeGMTarget = null; + + + m_kOpacityMap_GM.dispose(); + m_kOpacityMap_GM = null; + m_kOpacityMapTarget_GM.dispose(); + m_kOpacityMapTarget_GM = null; + + if (m_kSurfaceImage != null) { + m_kSurfaceImage.dispose(); + m_kSurfaceImage = null; + m_kSurfaceTarget.dispose(); + m_kSurfaceTarget = null; + } + + m_kLUT = null; + m_kPostfix = null; + if ( m_kHisto != null ) + { + for (final GraphicsImage element : m_kHisto) { + if ( element != null ) + { + element.dispose(); + } + } + m_kHisto = null; + } + m_akHistoTCoord = null; + } + + + /** + * This function is called when the user selects the Surface or Composite Surface volume rendering option. + * If the normals have already been initialized the function returns. Otherwise the function checks if the + * normals are available in a file on disk, and if so if they match the parameters (size, units, resolutions) of + * the original ModelImage. If the files match they are used and the Normal map is read from file. Otherwise this + * function launches the GPU-based Normal calculation. That calculation when finished calls the CopyNormalFiles + * which writes the calculated normals to disk and updates the Normal map on the GPU for rendering. + */ + public void GenerateNormalFiles( VolumeTriPlanarInterface parentFrame ) { + if ( m_bNormalsInit ) + { + return; + } + if ( !m_bNormalsInit ) + { + int dimX = m_kImage.getExtents().length > 0 ? m_kImage.getExtents()[0] : 1; + int dimY = m_kImage.getExtents().length > 1 ? m_kImage.getExtents()[1] : 1; + int dimZ = m_kImage.getExtents().length > 2 ? m_kImage.getExtents()[2] : 1; + ModelImage outputImage = new ModelImage( ModelStorageBase.ARGB_FLOAT, new int[]{dimX,dimY,dimZ}, "temp" ); + for (int i = 0; i < m_iTimeSteps; i++) { + OpenCLAlgorithmVolumeNormals oclNormals = new OpenCLAlgorithmVolumeNormals( m_kImage, outputImage, CL.CL_DEVICE_TYPE_GPU ); + oclNormals.setTime(i); + oclNormals.run(); + if ( m_kImage.isColorImage() ) + { + m_kNormal[i] = initVolumeData(outputImage, 0, m_kNormalMapTarget, outputImage.getImageName(), true, true); + } + else + { + addNormals(outputImage, i); + } + } + m_bNormalsInit = true; + + if ( m_kNormal != null) + { + m_kNormalMapTarget.SetImage(m_kNormal[m_iTimeSlice]); + m_kNormalMapTarget.Reload(true); + } + outputImage.disposeLocal(); + } + } + + /** + * Return the Color Map Texture. + * @return Volume color map Texture. + */ + public Texture GetColorMapTarget() { + return m_kColorMapTarget; + } + + /** + * Return the normalization factor for DDR rendering mode. + * @return normalization factor for DDR rendering mode. + */ + public float getDRRNorm() { + return m_fDRRNormalize; + } + + /** + * Return the Gradient Magnitude Texture. + * @return Gradient Magnitude Texture. + */ + public Texture GetGradientMapTarget() { + return m_kVolumeGMTarget; + } + + /** + * Returns true if the multi-histogram histogram texture has been initialized, false otherwise. + * @return true if the multi-histogram histogram texture has been initialized, false otherwise. + */ + public boolean isHistoInit() + { + return m_bHistoInit; + } + + /** + * Returns the multi-histogram histogram Texture. + * @return the multi-histogram histogram Texture. + */ + public Texture GetHistoTarget() { + return m_kHistoTarget; + } + + +// private ModelImage[] m_akHistogram; +// public ModelImage GetHistogram() { +// +// if ( !m_bHistoInit ) +// { +// SetGradientMagnitude(null, true, m_kPostfix); +// } +// if ( m_akHistogram == null ) +// { +// m_akHistogram = new ModelImage[m_akImages.length]; +// } +// if ( m_akHistogram[m_iTimeSlice] == null ) +// { +// m_akHistogram[m_iTimeSlice] = new ModelImage(ModelStorageBase.INTEGER, new int[]{256,256}, "JointHisto" + m_iTimeSlice); +// try { +// m_akHistogram[m_iTimeSlice].importData(m_kHisto[m_iTimeSlice].GetData()); +// } catch (IOException e) { +// e.printStackTrace(); +// } +// } +// return m_akHistogram[m_iTimeSlice]; +// } + + /** + * Return the texture coordinates for the multi-histogram histogram texture. + * @return the texture coordinates for the multi-histogram histogram texture. + */ + public Vector2f[] GetHistoTCoords() { + return m_akHistoTCoord; + } + + public ModelImage GetGradientMagnitudeImage() + { + return m_kImageGM; + } + + public Vector2f GetGradientMagnitudeMinMax() + { + return m_akGradientMagMinMax[m_iTimeSlice]; + } + + public float GetGradientMagnitudeMin() + { + return m_akGradientMagMinMax[m_iTimeSlice].X; + } + + public float GetGradientMagnitudeMax() + { + return m_akGradientMagMinMax[m_iTimeSlice].Y; + } + + /** + * Return the ModelImage volume data. + * @return ModelImage volume data. + */ + public ModelImage GetImage() { + return m_kImage; + } + + /** + * Return the ModelImage LUT. + * @return Volume LUT. + */ + public ModelLUT GetLUT() { + return m_kLUT; + } + + /** + * Return the Normal map Texture. + * @return Normal map Texture. + */ + public Texture GetNormalMapTarget() { + return m_kNormalMapTarget; + } + + public Texture GetScratchTarget() { + return m_kScratchTarget; + } + + /** + * Return the gradient magnitude opacity transfer function Texture. + * @return gradient magnitude opacity transfer function Texture. + */ + public Texture GetOpacityMapGMTarget() { + return m_kOpacityMapTarget_GM; + } + + /** + * Return the postfix for this VolumeImage. + * @return postfix for this VolumeImage. + */ + public String GetPostfix() { + return m_kPostfix; + } + + /** + * Return the Volume RGBT. + * @return Volume RGBT. + */ + public ModelStorageBase getLUT() { + return (m_kImage != null ) ? m_kImage.isColorImage() ? m_kRGBT : m_kLUT : null; + } + + /** + * Return the Volume RGBT. + * @return Volume RGBT. + */ + public ModelRGB GetRGB() { + return m_kRGBT; + } + + + public float GetTransferedValue( int x, int y, int z ) + { + int dimX = m_kImage.getExtents().length > 0 ? m_kImage.getExtents()[0] : 1; + int dimY = m_kImage.getExtents().length > 1 ? m_kImage.getExtents()[1] : 1; + int dimZ = m_kImage.getExtents().length > 2 ? m_kImage.getExtents()[2] : 1; + if ( x < 0 || x >= dimX || y < 0 || y >= dimY || z < 0 || z >= dimZ ) return -1; + + if ( m_kImage.isColorImage() ) { + float r = m_kRGBT.getROn() ? TransferValue(m_kImage.getFloat(x, y, z, 1)) : -1; + float g = m_kRGBT.getGOn() ? TransferValue(m_kImage.getFloat(x, y, z, 2)) : -1; + float b = m_kRGBT.getBOn() ? TransferValue(m_kImage.getFloat(x, y, z, 3)) : -1; + return Math.max( r, Math.max(g, b)); + } + float value = m_kImage.getFloat(x, y, z); + return TransferValue(value); + } + + private float TransferValue(float value) { + float min = (float) m_kImage.getMin(); + float max = (float) m_kImage.getMax(); + float diff = max - min; + byte index = 0; + if ( (diff > 1) && (diff <= 255) ) + { + index = (byte)(((value - min)/diff) * diff); + } + else + { + index = (byte)(((value - min)/diff) * 255); + } + if ( (index >= 0) && (index < 255) && (m_kColorMap != null) && (m_kColorMap.GetData() != null) ) + { + byte r = m_kColorMap.GetData()[index * 4 + 0]; + byte g = m_kColorMap.GetData()[index * 4 + 1]; + byte b = m_kColorMap.GetData()[index * 4 + 2]; + byte a = m_kColorMap.GetData()[index * 4 + 3]; + return Math.max(r*a, Math.max(g*a, b*a)); + } + return -1; + } + + + + /** + * The ModelImage Volume max-scale factor. + * @return Volume max-scale factor. + */ + public float GetScaleMax() { + return m_fMax; + } + + public int GetMaxExtent() + { + return m_iMaxExtent; + } + + /** + * The ModelImage Volume x-scale factor. + * @return Volume x-scale factor. + */ + public float GetScaleX() { + return m_fX; + } + + /** + * The ModelImage Volume y-scale factor. + * @return Volume y-scale factor. + */ + public float GetScaleY() { + return m_fY; + } + + /** + * The ModelImage Volume z-scale factor. + * @return Volume z-scale factor. + */ + public float GetScaleZ() { + return m_fZ; + } + + /** + * Return the surface mask Texture. + * @return surface mask Texture. + */ + public Texture GetSurfaceTarget() { + return m_kSurfaceTarget; + } + + + + /** A vector of BitSet masks, one for each surface loaded into the viewer. */ + protected Vector surfaceMask; + /** A vector of the mask names, so they can be accessed by name: */ + protected Vector surfaceNames; + /** A vector of BitSet masks, one for each surface loaded into the viewer. */ + protected Vector surfaceColor; + /** + * Add a new surface mask. + * @param name surface name. + * @param mask surface mask volume. + */ + public void setSurfaceMask(String name, ColorRGB color, BitSet mask) + { + if ( surfaceMask == null ) + { + surfaceMask = new Vector(); + surfaceNames = new Vector(); + surfaceColor = new Vector(); + } + surfaceMask.add(mask); + surfaceNames.add(name); + surfaceColor.add(color); + updateMask(); + } + + private void updateMask() + { + boolean bUpdate = false; + final int iXBound = m_kImage.getExtents()[0]; + final int iYBound = m_kImage.getExtents()[1]; + final int iZBound = m_kImage.getExtents()[2]; + int length = iXBound * iYBound * iZBound; + for ( int i = 0; i < length; i++ ) + { + boolean color = false; + for ( int surface = 0; surface < surfaceMask.size(); surface++ ) + { + if ( surfaceMask.elementAt(surface).get(i) ) + { + m_kSurfaceImage.GetData()[i * 4 + 0] = (byte) (surfaceColor.elementAt(surface).R * 255); + m_kSurfaceImage.GetData()[i * 4 + 1] = (byte) (surfaceColor.elementAt(surface).G * 255); + m_kSurfaceImage.GetData()[i * 4 + 2] = (byte) (surfaceColor.elementAt(surface).B * 255); + m_kSurfaceImage.GetData()[i * 4 + 3] = (byte) (255); + bUpdate = true; + color = true; + } + } + if ( !color ) + { + m_kSurfaceImage.GetData()[i * 4 + 0] = (byte) (0); + m_kSurfaceImage.GetData()[i * 4 + 1] = (byte) (0); + m_kSurfaceImage.GetData()[i * 4 + 2] = (byte) (0); + m_kSurfaceImage.GetData()[i * 4 + 3] = (byte) (0); + } + } + if ( bUpdate ) + { + m_kSurfaceTarget.Reload(true); + } + } + + /** + * Delete the surface mask, using the name of the mask as reference. + * @param name the surface name. + */ + public void removeSurfaceMask(String name) + { + boolean bUpdate = false; + if ( surfaceMask != null && surfaceNames != null) + { + if ( surfaceNames.contains(name) ) + { + surfaceMask.remove( surfaceNames.indexOf(name) ); + surfaceColor.remove( surfaceNames.indexOf(name) ); + surfaceNames.remove(name); + bUpdate = true; + } + } + if ( bUpdate ) + { + updateMask(); + m_kSurfaceTarget.Reload(true); + } + } + + /** + * Delete the surface mask, using the name of the mask as reference. + * @param name the surface name. + */ + public void setSurfaceMaskColor(String name, ColorRGB color) + { + boolean bUpdate = false; + if ( surfaceMask != null && surfaceNames != null) + { + if ( surfaceNames.contains(name) ) + { + surfaceColor.elementAt( surfaceNames.indexOf(name) ).Copy(color); + bUpdate = true; + } + } + if ( bUpdate ) + { + updateMask(); + } + } + + + /** + * Returns the current rendered time-slice for 4D images. Otherwise returns 0. + * @return the current rendered time-slice for 4D images. Otherwise returns 0. + */ + public int GetTimeSlice() { + return m_iTimeSlice; + } + + /** + * Return the Texture containing the volume data. + * @return Texture containing the volume data. + */ + public Texture GetVolumeTarget() { + return m_kVolumeTarget; + } + + /** + * Return the Buffer containing the volume data, which is stored in the Texture GrapicsImage. + * @return Buffer containing the volume data. + */ + public Buffer GetVolumeTargetBuffer() { + return m_kVolumeTarget.GetImage().GetDataBuffer(); + } + + /** + * Initialize the GraphicsImage for the opacity lookup table. + * + * @param kImage the ModelImage the opacity transfer function applies to. + * @param kPostfix the string postfix to concatenate to the "OpacityMap" image name. + * @return GraphicsImage, the new GraphicsImage storing opacity lookup table. + */ + public GraphicsImage InitOpacityMap(final ModelImage kImage, final String kPostFix) { + final int iLutHeight = 256; + final float[] afData = new float[iLutHeight]; + final float fRange = (float) (kImage.getMax() - kImage.getMin()); + final float fStep = fRange / iLutHeight; + float fDataValue = (float) kImage.getMin(); + for (int i = 0; i < iLutHeight; i++) { + afData[i] = (float) (iLutHeight * (kImage.getMax() - fDataValue) / fRange); + fDataValue += fStep; + } + + return new GraphicsImage(GraphicsImage.FormatMode.IT_L8, iLutHeight, afData, + new String("OpacityMap" + kPostFix)); + } + + /** + * Return true if the Volume image is a color image. + * + * @return true if the Volume image is a color image. + */ + public boolean IsColorImage() { + return m_kImage.isColorImage(); + } + + /** + * Release the Textures containing the volume data. Once Textures are released, they will be re-loaded onto the GPU + * during the next frame. + */ + public void ReleaseVolume() { + m_kVolumeTarget.Reload(true); + } + + + + /** + * Called when the user selects the Gradient Magnitude option or the Multi-Histogram option + * in the Volume Renderer. + * @param kGradientMagnitude pre-computed GradientMagnitude image or null + * @param bComputeLaplace when true the Laplace image and multi-histogram histogram Textures are computed. + * @param kPostfix GraphicsImage postfix string. + */ + public void SetGradientMagnitude(ModelImage kGradientMagnitude, boolean bComputeLaplace, String kPostfix ) + { + int start = 0; + int end = m_kImage.getNDims() > 3 ? m_iTimeSteps : 1; + if ( !m_bGMInit ) + { + try { + // System.err.println( "SetGradientMagnitude " + 0 ); + m_kImageGM = getGradientMagnitude( m_kImage, 0 ); + if ( m_kImageGM != null ) + { + m_kVolumeGM[0] = createGM_Laplace(m_kImageGM, null, m_kVolumeGM[0], 0, true); + if ( m_kVolumeGM[0] != null ) + { + m_akGradientMagMinMax[0] = new Vector2f( (float)m_kImageGM.getMin(), (float)m_kImageGM.getMax() ); + m_kVolumeGMTarget.SetImage(m_kVolumeGM[0]); + m_kVolumeGMTarget.Reload(true); + m_bGMInit = true; + } + } + for (int i = 1; i < m_iTimeSteps; i++) + { + // System.err.println( "SetGradientMagnitude " + i ); + ModelImage gmImage = getGradientMagnitude( m_kImage, i ); + if ( gmImage != null ) + { + m_kVolumeGM[i] = createGM_Laplace(gmImage, null, m_kVolumeGM[i], i, true); + if ( m_kVolumeGM[i] != null ) + { + m_akGradientMagMinMax[i] = new Vector2f( (float)gmImage.getMin(), (float)gmImage.getMax() ); + gmImage.disposeLocal(); + end = i + 1; + } + } + } + } catch ( java.lang.OutOfMemoryError e ) {} + } + + if ( m_bGMInit && bComputeLaplace && (m_kImageGM != null)) + { + GenerateHistogram(m_kVolume, m_kVolumeGM, kPostfix, start, end ); + } + } + + /** + * Sets the ModelRGB for the iImage. + * + * @param kRGBT new ModelRGB + */ + public void SetRGBT(final ModelRGB kRGBT) { + if (kRGBT == null) { + return; + } + ModelLUT.exportIndexedLUTMin(kRGBT, m_kColorMap.GetData()); + m_kColorMapTarget.Reload(true); + m_kRGBT = kRGBT; + } + + /** + * Sets the time slices for 4D data. + * @param iSlice new time slice value. + */ + public void SetTimeSlice(final int iSlice) { + if (m_iTimeSlice != iSlice) { + m_iTimeSlice = iSlice; + update4D(); + } + } + + /** + * Updates the current time slice. + * @param bForward when true the time advances on step forward or wraps to the beginning. + * When false the time moves backward. + */ + public void update4D(final boolean bForward) { + if (m_iTimeSteps == 1) { + return; + } + if (bForward) { + m_iTimeSlice++; + } else { + m_iTimeSlice--; + } + if (m_iTimeSlice >= m_iTimeSteps) { + m_iTimeSlice = 0; + } + if (m_iTimeSlice < 0) { + m_iTimeSlice = m_iTimeSteps - 1; + } + + update4D(); + } + + /** + * Update the image data. + * + * @param kImage the modified ModelImage + * @param bCopytoCPU when true the data is copied from the GPU GraphicsImage into the ModelImage + */ + public void UpdateData(final ModelImage kImage, boolean reload) { + m_kImage = kImage; + if ( m_kVolume == null ) { + m_kPostfix = ""; + init(null, 0, false); + return; + } + initLUT(); + if ( reload ) + { + if ( m_kVolume[m_iTimeSlice] != null ) + { + m_kVolume[m_iTimeSlice].dispose(); + } + m_kVolumeTarget.Remove(); + m_kVolume[m_iTimeSlice] = initVolumeData(m_kImage, m_iTimeSlice, m_kVolumeTarget, m_kImage.getImageName(), true, false); + m_kVolumeTarget.SetImage(m_kVolume[m_iTimeSlice]); + } + else + { + m_kVolume[m_iTimeSlice] = resetVolumeData(m_kImage, m_iTimeSlice, m_kVolume[m_iTimeSlice], m_kVolumeTarget, m_kImage + .getImageName(), true, false); + } + InitScale(); + } + + + /** + * Changes the underlying image data and LUT. If the new image data is a different size than + * then previous one, recreate the volume image on the GPU, otherwise just overwrite it with + * the new data. + * @param kImage + * @param kLUT + * @param reload + */ + public void UpdateData(final ModelImage kImage, ModelLUT kLUT, boolean reload) { + if ( kLUT == null && !kImage.isColorImage()) + { + UpdateData(kImage, reload); + return; + } + m_kImage = kImage; + m_kLUT = kLUT; + if ( reload ) + { + if ( m_kVolume[m_iTimeSlice] != null ) + { + m_kVolume[m_iTimeSlice].dispose(); + } + m_kVolumeTarget.Remove(); + m_kVolume[m_iTimeSlice] = initVolumeData(m_kImage, m_iTimeSlice, m_kVolumeTarget, m_kImage.getImageName(), true, false); + m_kVolumeTarget.SetImage(m_kVolume[m_iTimeSlice]); + } + else + { + m_kVolume[m_iTimeSlice] = resetVolumeData(m_kImage, m_iTimeSlice, m_kVolume[m_iTimeSlice], m_kVolumeTarget, m_kImage + .getImageName(), true, false); + } + InitScale(); + } + + /** + * Update the LUT for the ModelImage. + * + * @param kLUT new LUT for ModelImage. + */ + public void UpdateImages(final ModelLUT kLUT) { + if (kLUT != null) { + VolumeImage.UpdateImages(m_kColorMapTarget, m_kColorMap, kLUT); + m_kLUT = kLUT; + } + } + + /** + * Update the LUT for the ModelImage. + * + * @param kLUT new LUT for ModelImage. + */ + public void UpdateImages(final ModelStorageBase kLUT) { + if ( (kLUT != null) && (kLUT instanceof ModelLUT)) { + VolumeImage.UpdateImages(m_kColorMapTarget, m_kColorMap, (ModelLUT)kLUT); + m_kLUT = (ModelLUT)kLUT; +// System.err.println("UpdateImages " + m_kColorMapTarget.GetName() + " " + kLUT. +// System.err.println(" " + m_kColorMapTarget.GetID() ); + + } + if ( (kLUT != null) && (kLUT instanceof ModelRGB)) { + ModelLUT.exportIndexedLUTMin((ModelRGB)kLUT, m_kColorMap.GetData()); + m_kColorMapTarget.Reload(true); + m_kRGBT = (ModelRGB)kLUT; + } + } + + /** + * Update the transfer function for the image iImage. + * + * @param kTransfer the new opacity transfer function + * @param iImage the image to modify (0 = volume image, 2 = gradient mag) + * @param kImage GradientMagitude image. + * @return boolean true when updated, false otherwise. + */ + public boolean UpdateImages(final TransferFunction kTransfer, final int iImage, final ModelImage kImage) { + if (iImage == 0) { + return UpdateImages2(m_kImage, m_kColorMapTarget, m_kColorMap, kTransfer); + } else if ( (iImage == 2) && (kImage != null) && (m_kOpacityMapTarget_GM != null) && (m_kOpacityMap_GM != null)) { + return UpdateImages(kImage, m_kOpacityMapTarget_GM, m_kOpacityMap_GM, kTransfer); + } + return false; + } + + /** + * In order to map line integrals of image intensity to RGB colors where each color channel is 8 bits, it is + * necessary to make sure that the integrals are in [0,255]. Producing a theoretical maximum value of a line + * integral is not tractable in an application. This method constructs an approximate maximum by integrating along + * each line of voxels in the image with line directions parallel to the coordinate axes. The 'processRay' call + * adjusts the line integrals using the estimate, but still clamps the integrals to 255 since the estimate might not + * be the true maximum. + * + * @return float Integral normalization factor. + */ + protected float computeIntegralNormalizationFactor() { + final int iXBound = m_kImage.getExtents()[0]; + final int iYBound = m_kImage.getExtents()[1]; + final int iZBound = m_kImage.getExtents()[2]; + + byte[] aucData = null; + int iSize = iXBound * iYBound * iZBound; + if (m_kImage.isColorImage()) { + iSize *= 4; + } + + aucData = new byte[iSize]; + + try { + m_kImage.exportDataUseMask(0, iSize, false, aucData); + } catch (final IOException e) { + e.printStackTrace(); + } + + // compute image normalization factor + int iX, iY, iZ, iBase, iSteps; + float fMaxIntegral = 0.0f; + float fTStep, fIntegral; + + // fix y and z, integrate over x + for (iY = 0; iY < iYBound; iY++) { + + for (iZ = 0; iZ < iZBound; iZ++) { + iBase = iXBound * (iY + (iYBound * iZ)); + iSteps = iXBound - 1; + fIntegral = 0.5f * ( (aucData[iBase] & 0x0ff) + (aucData[iBase + iSteps] & 0x0ff)); + fTStep = 1.0f / iSteps; + + for (iX = 1; iX < iSteps; iX++) { + fIntegral += (aucData[iBase + iX] & 0x0ff); + } + + fIntegral *= fTStep; + + if (fIntegral > fMaxIntegral) { + fMaxIntegral = fIntegral; + } + } + } + final int iXYProduct = iXBound * iYBound; + // fix x and z, integrate over y + for (iX = 0; iX < iXBound; iX++) { + + for (iZ = 0; iZ < iZBound; iZ++) { + iBase = iX + (iXYProduct * iZ); + iSteps = iYBound - 1; + fIntegral = 0.5f * ( (aucData[iBase] & 0x0ff) + (aucData[iBase + (iXBound * iSteps)] & 0x0ff)); + fTStep = 1.0f / iSteps; + + for (iY = 1; iY < iSteps; iY++) { + fIntegral += (aucData[iBase + (iXBound * iY)] & 0x0ff); + } + + fIntegral *= fTStep; + + if (fIntegral > fMaxIntegral) { + fMaxIntegral = fIntegral; + } + } + } + + // fix x and y, integrate over z + for (iX = 0; iX < iXBound; iX++) { + + for (iY = 0; iY < iYBound; iY++) { + iBase = iX + (iXBound * iY); + iSteps = iZBound - 1; + fIntegral = 0.5f * ( (aucData[iBase] & 0x0ff) + (aucData[iBase + (iXYProduct * iSteps)] & 0x0ff)); + fTStep = 1.0f / iSteps; + + for (iZ = 1; iZ < iSteps; iZ++) { + fIntegral += (aucData[iBase + (iXYProduct * iZ)] & 0x0ff); + } + + fIntegral *= fTStep; + + if (fIntegral > fMaxIntegral) { + fMaxIntegral = fIntegral; + } + } + } + aucData = null; + return (fMaxIntegral > 0.0f) ? (1.0f / fMaxIntegral) : 0.00f; + } + + /** + * Checks that the two input images match extents, units of measure and resolutions. The images + * may had different sizes (3D or 4D) the first 3-dimensions must match. + * @param kImage1 + * @param kImage2 + * @return true if the images match extends, units and resolutions. + */ + public static boolean checkImage(ModelImage kImage1, ModelImage kImage2 ) + { + for ( int i = 0; i < Math.min( kImage1.getExtents().length, kImage2.getExtents().length ); i++ ) + { + if ( kImage1.getExtents()[i] != kImage2.getExtents()[i] ) + { + return false; + } + if ( kImage1.getUnitsOfMeasure()[i] != kImage2.getUnitsOfMeasure()[i] ) + { + return false; + } + if ( kImage1.getResolutions(0)[i] != kImage2.getResolutions(0)[i] ) + { + return false; + } + } + return true; + } + + /** + * Generate 2D histogram from the input image and the gradient-magnitude + * + * @param kImage input GraphicsImage containing the ModelImage data + * @param kImageGM input GraphcisImage containing the Gradient Magnitude data. + * @param kPostFix post-fix for the image name. + */ + private void GenerateHistogram(final GraphicsImage[] kImage, final GraphicsImage[] kImageGM, final String kPostFix, + int start, int end ) + { + int iTMinX = 255, iTMaxX = 0; + int iTMinY = 255, iTMaxY = 0; + float max = Float.MIN_VALUE; + float min = Float.MAX_VALUE; + m_kHisto = new GraphicsImage[m_iTimeSteps]; + for (int t = start; t < end; t++) { + float[] afCount = new float[256 * 256]; + for (int i = 0; i < 256 * 256; i++) { + afCount[i] = 0; + } + + int a1; + int a2; + final byte[] abHistoData = kImageGM[t].GetData(); + final byte[] abData = kImage[t].GetData(); + if (m_kImage.isColorImage()) { + int iHisto = 0; + for (int i = 0; i < abData.length; i += 4) { + int iR = (abData[i]); + int iG = (abData[i + 1]); + int iB = (abData[i + 2]); + //a1 = (iR * 0.299 + iG * 0.587 + iB * 0.114); + a1 = (iR + iG + iB)/3; + a1 = (a1 & 0x00ff); + + iR = (abHistoData[i]); + iG = (abHistoData[i + 1]); + iB = (abHistoData[i + 2]); + //a2 = (short) (iR * 0.299 + iG * 0.587 + iB * 0.114); + a2 = (iR + iG + iB)/3; + a2 = (a2 & 0x00ff); + afCount[a1 + a2 * 256] += 1; + iHisto++; + } + } + else { + int iHisto = 0; + for (int i = 0; i < abData.length; i += 4) { + a1 = abData[i]; + a1 = (a1 & 0x00ff); + a2 = (abHistoData[iHisto]); + a2 = (a2 & 0x00ff); + afCount[a1 + a2 * 256] += 1; + iHisto += 4; + } + } + max = Float.MIN_VALUE; + min = Float.MAX_VALUE; + for (int i = 0; i < 256 * 256; ++i) { + afCount[i] = (float) Math.log(afCount[i]+1); + max = Math.max(afCount[i], max); + min = Math.min(afCount[i], min); + } + //System.err.println( min + " " + max ); + final byte[] abHisto = new byte[256 * 256]; + //int maxB = Integer.MIN_VALUE; + //int minB = Integer.MAX_VALUE; + for (int i = 0; i < 256 * 256; ++i) { + abHisto[i] = new Float((afCount[i] / max) * 255f).byteValue(); + //maxB = ( iVal > maxB ) ? iVal : maxB; + //minB = ( iVal < minB ) ? iVal : minB; + } + afCount = null; + + int iMinX = 255, iMaxX = 0; + int iIndex = 0; + for (int i = 0; i < 256; i++) { + for (int j = 0; j < 256; j++) { + iIndex = i * 256 + j; + if (abHisto[iIndex] > 50) { + if (iMinX > j) { + iMinX = j; + } + if (j > iMaxX) { + iMaxX = j; + } + } + } + } + + int iMinY = 255, iMaxY = 0; + for (int j = 0; j < 256; j++) { + for (int i = 0; i < 256; i++) { + iIndex = i * 256 + j; + if (abHisto[iIndex] > 50) { + if (iMinY > i) { + iMinY = i; + } + if (i > iMaxY) { + iMaxY = i; + } + } + } + } + if (iTMinX > iMinX) { + iTMinX = iMinX; + } + if (iTMaxX < iMaxX) { + iTMaxX = iMaxX; + } + + if (iTMinY > iMinY) { + iTMinY = iMinY; + } + if (iTMaxY < iMaxY) { + iTMaxY = iMaxY; + } + + // iMinX = 0; iMaxX = 255; + // iMinY = 0; iMaxY = 255; + + m_kHisto[t] = new GraphicsImage(GraphicsImage.FormatMode.IT_L8, 256, 256, (byte[]) null, new String( + "VolumeImageHisto" + kPostFix)); + m_kHisto[t].SetData(abHisto, 256, 256); + /* + ModelImage kTestHisto2D = new ModelImage( ModelStorageBase.UBYTE, new int[]{256,256}, "Histo2D" ); + try { + kTestHisto2D.importData(abHisto); + } catch (IOException e) { + e.printStackTrace(); + } + kTestHisto2D.calcMinMax(); + new ViewJFrameImage( kTestHisto2D ); */ + } + + m_kHistoTarget = new Texture(); + m_kHistoTarget.SetImage(m_kHisto[0]); + m_kHistoTarget.SetShared(true); + m_kHistoTarget.SetFilterType(Texture.FilterType.LINEAR); + m_kHistoTarget.SetWrapType(0, Texture.WrapType.CLAMP_BORDER); + m_kHistoTarget.SetWrapType(1, Texture.WrapType.CLAMP_BORDER); + m_kHistoTarget.SetWrapType(2, Texture.WrapType.CLAMP_BORDER); + + iTMinX = 0; + iTMaxX = Math.max( iTMaxX, iTMaxY ); + m_akHistoTCoord = new Vector2f[4]; + m_akHistoTCoord[0] = new Vector2f(iTMinX / 255.0f, iTMinX / 255.0f); + m_akHistoTCoord[1] = new Vector2f(iTMaxX / 255.0f, iTMinX / 255.0f); + m_akHistoTCoord[2] = new Vector2f(iTMaxX / 255.0f, iTMaxX / 255.0f); + m_akHistoTCoord[3] = new Vector2f(iTMinX / 255.0f, iTMaxX / 255.0f); + //m_akHistoTCoord[0] = new Vector2f(0f, 0f); + //m_akHistoTCoord[1] = new Vector2f(1f, 0f); + //m_akHistoTCoord[2] = new Vector2f(1f, 1f); + //m_akHistoTCoord[3] = new Vector2f(0f, 1f); + m_bHistoInit = true; + } + + /** + * Calculates and stores the gradient magnitude images (3D or 4D) for the input image. Or reads from disk. + * The data is stored in the GraphicsImage data structures and will be passed to the GPU to use in rendering. + * + * @param kImage input image + * @param kGradientMagnitude input Gradient Magnitude image, or null. + * @param bComputeLaplace when true the Laplace image is also calculated or read from dis. + private void GradientMagnitudeImage(final ModelImage kImage, ModelImage kGradientMagnitude, + boolean bComputeLaplace) { + + + if ( !m_bGMInit ) + { + ModelImage[] kImageGM = new ModelImage[m_iTimeSteps]; + String[] kImageName = new String[m_iTimeSteps]; + for (int i = 0; i < m_iTimeSteps; i++) { + kImageName[i] = ModelImage.makeImageName(kImage.getFileInfo(0).getFileName(), new String("_GM_" + i)); + + if ( kGradientMagnitude != null && checkImage(kImage, kGradientMagnitude )) + { + m_kVolumeGM[i] = VolumeImage.UpdateData(kGradientMagnitude, i, null, m_kVolumeGM[i], + m_kVolumeGMTarget, m_kVolumeGM[i].GetName(), true, false); + ModelImage.saveImage( kGradientMagnitude, kImageName[i] + ".xml", m_kDir ); + } + else + { + kImageGM[i] = ReadFromDisk(kImageName[i] + ".xml", m_kDir); + if ( kImageGM[i] != null && !checkImage(kImage, kImageGM[i] ) ) + { + kImageGM[i].disposeLocal(); + kImageGM[i] = null; + } + if (kImageGM[i] == null) { + JDialogGradientMagnitude kCalcMagnitude = new JDialogGradientMagnitude(null, m_akImages[i]); + kCalcMagnitude.setVisible(false); + kCalcMagnitude.setOutputNewImage(true); + kCalcMagnitude.setDisplayProgressBar(true); + kCalcMagnitude.setSeparateThread(false); + kCalcMagnitude.setSeparable(true); + kCalcMagnitude.setUseOCL(true); + kCalcMagnitude.actionPerformed(new ActionEvent(this, 0, "OK")); + kImageGM[i] = kCalcMagnitude.getResultImage(); + kCalcMagnitude = null; + } + if (kImageGM[i] == null) { + System.err.println("Gradient magnitude calculation returned null"); + m_kVolumeGM[i] = VolumeImage.UpdateData(kImage, i, null, m_kVolumeGM[i], m_kVolumeGMTarget, kImageName[i], true, false); + } else { + kImageGM[i].calcMinMax(); + m_akGradientMagMinMax[i] = new Vector2f( (float)kImageGM[i].getMin(), (float)kImageGM[i].getMax() ); + + if ( !( bComputeLaplace && !m_kImage.isColorImage() ) ) + { + m_kVolumeGM[i] = VolumeImage.UpdateData(kImageGM[i], 0, null, m_kVolumeGM[i], m_kVolumeGMTarget, kImageName[i], true, false); + } + } + } + } + + + if ( bComputeLaplace && !m_kImage.isColorImage() ) + { + for (int i = 0; i < m_iTimeSteps; i++) { + final String kImageNameL = ModelImage.makeImageName(kImage.getFileInfo(0).getFileName(), new String( + "_Laplacian_" + i)); + ModelImage kImageGMGM = null; + kImageGMGM = ReadFromDisk(kImageNameL + ".xml", m_kDir); + if ( kImageGMGM != null && !checkImage(kImage, kImageGMGM ) ) + { + kImageGMGM.disposeLocal(); + kImageGMGM = null; + } + if (kImageGMGM == null) { + final JDialogLaplacian kCalcLaplacian = new JDialogLaplacian(null, m_akImages[i]); + kCalcLaplacian.setVisible(false); + kCalcLaplacian.setOutputNewImage(true); + kCalcLaplacian.setDisplayProgressBar(true); + kCalcLaplacian.setSeparateThread(false); + kCalcLaplacian.setUseOCL(true); + kCalcLaplacian.setSeparable(true); + kCalcLaplacian.actionPerformed(new ActionEvent(this, 0, "OK")); + kImageGMGM = kCalcLaplacian.getResultImage(); + } + if (kImageGMGM != null) { + m_kVolumeGM[i] = createGM_Laplace(kImageGM[i], kImageGMGM, m_kVolumeGM[i], i, true); + } else { + m_kVolumeGM[i] = VolumeImage.UpdateData(kImageGM[i], 0, null, m_kVolumeGM[i], m_kVolumeGMTarget, kImageName[i], true, false); + } + final ViewJFrameImage kImageFrame = ViewUserInterface.getReference().getFrameContainingImage(kImageGMGM); + if (kImageFrame != null) { + kImageFrame.close(); + } else if (kImageGMGM != null) { + kImageGMGM.disposeLocal(); + kImageGMGM = null; + } + } + } + + m_bGMInit = true; + m_kVolumeGMTarget.SetImage(m_kVolumeGM[0]); + m_kVolumeGMTarget.Reload(true); + + for ( int i = 0; i < kImageGM.length; i++ ) + { + if (kImageGM[i] != null) { + + kImageGM[i].setImageDirectory( m_kDir ); + kImageGM[i].setImageName( kImageName[i] + ".xml" ); + ModelImage.saveImage(kImageGM[i], kImageName[i] + ".xml", m_kDir ); + + + final ViewJFrameImage kImageFrame = ViewUserInterface.getReference().getFrameContainingImage(kImageGM[i]); + if (kImageFrame != null) { + kImageFrame.close(); + } + + kImageGM[i].disposeLocal(); + kImageGM[i] = null; + } + } + } + } + */ + + public static ModelImage getGradientMagnitude( ModelImage kImage, int i ) + { + if ( kImage == null ) + { + return null; + } + int index = kImage.getExtents()[2] / 2; + float xRes = kImage.getFileInfo(index).getResolutions()[0]; + float zRes = kImage.getFileInfo(index).getResolutions()[2]; + + float correction = xRes / zRes; + float[] sigmas = new float[]{1f,1f,correction}; + + int dimX = kImage.getExtents().length > 0 ? kImage.getExtents()[0] : 1; + int dimY = kImage.getExtents().length > 1 ? kImage.getExtents()[1] : 1; + int dimZ = kImage.getExtents().length > 2 ? kImage.getExtents()[2] : 1; + ModelImage outputImage = new ModelImage( kImage.getDataType(), new int[]{dimX,dimY,dimZ}, "temp" ); + AlgorithmGradientMagnitudeSep gradientMagAlgo = new AlgorithmGradientMagnitudeSep( kImage, sigmas, true, false ); +// OpenCLAlgorithmGradientMagnitude gradientMagAlgo = new OpenCLAlgorithmGradientMagnitude(outputImage, kImage, sigmas, +// true, true, false); + gradientMagAlgo.setRed(true); + gradientMagAlgo.setGreen(true); + gradientMagAlgo.setBlue(true); + gradientMagAlgo.setRunningInSeparateThread(false); + gradientMagAlgo.run(); + float[] resultBuffer = gradientMagAlgo.getResultBuffer(); + try { + outputImage.importData(0, resultBuffer, true); + } catch (IOException e) {} +// gradientMagAlgo.gradientMagnitudeSep3D( i ); + gradientMagAlgo.finalize(); + gradientMagAlgo = null; + return outputImage; + } + + +// private ModelImage getLaplace( ModelImage kImage, int i ) +// { +// String kImageName = ModelImage.makeImageName(kImage.getFileInfo(0).getFileName(), new String("_Laplacian_" + i)); +// ModelImage kImageL = ReadFromDisk(kImageName + ".xml", m_kDir); +// if ( kImageL != null && !checkImage(kImage, kImageL ) ) +// { +// kImageL.disposeLocal(); +// kImageL = null; +// } +// if (kImageL == null) { +// JDialogLaplacian kCalcLaplacian = new JDialogLaplacian(null, m_akImages[i]); +// kCalcLaplacian.setVisible(false); +// kCalcLaplacian.setOutputNewImage(true); +// kCalcLaplacian.setDisplayProgressBar(true); +// kCalcLaplacian.setSeparateThread(false); +// kCalcLaplacian.setUseOCL(true); +// kCalcLaplacian.setSeparable(true); +// kCalcLaplacian.actionPerformed(new ActionEvent(this, 0, "OK")); +// kImageL = kCalcLaplacian.getResultImage(); +// kCalcLaplacian = null; +// +// kImageL.setImageDirectory( m_kDir ); +// kImageL.setImageName( kImageName + ".xml" ); +// JDialogBase.updateFileInfo( kImage, kImageL ); +// ModelImage.saveImage(kImageL, kImageName + ".xml", m_kDir ); +// final ViewJFrameImage kImageFrame = ViewUserInterface.getReference().getFrameContainingImage(kImageL); +// if (kImageFrame != null) { +// kImageFrame.setVisible(false); +// } +// } +// return kImageL; +// } + + /** + * Initialize the VolumeImage with the ModelImage data. + * @param kProgress progress bar + * @param iProgress progress bar increment + */ + private void init(final ViewJProgressBar kProgress, final int iProgress, boolean initGradientMagnitude) { + // Create LUTS for the ModelImage: + initLUT(); + // Initialize Texture Maps: + if ( !m_kImage.isColorImage() ) + { + initImages(); + } + else + { + initImagesColor(); + } + if ( initGradientMagnitude ) + { + SetGradientMagnitude(null, true, m_kPostfix); + } + if (kProgress != null) { + kProgress.updateValueImmed(kProgress.getValue() + iProgress); + } + } + + /** + * Intializes the Textures and GraphicsImages used to render the ModelImage this + * VolumeImage represents. + */ + private void initImages() { + m_fDRRNormalize = computeIntegralNormalizationFactor(); + // Initialize Color Map GraphicsImage: + m_kColorMap = initColorMap(); + // Initialize Opacity Map for the GradientMagnitude image: + m_kOpacityMap_GM = InitOpacityMap(m_kImage, new String(m_kPostfix + "_GM")); + + final int iXBound = m_kImage.getExtents()[0]; + final int iYBound = m_kImage.getExtents()[1]; + final int iZBound = m_kImage.getExtents()[2]; + + /* + * Map the ModelImage volume data to a texture image, including for the ModelImage gradient magnitude data: + */ + final int[] aiExtents = m_kImage.getExtents(); + final int iNDims = aiExtents.length; + String kImageName; + GraphicsImage.FormatMode type = GraphicsImage.FormatMode.IT_RGBA8888 ; + + + if (iNDims == 3) { // ModelImage is 3D: + m_iTimeSteps = 1; + } + else { // ModelImage is 4D: + m_iTimeSteps = aiExtents[3]; + } + // Allocate a 3D GraphicsImage for each 3D Volume + m_kVolume = new GraphicsImage[m_iTimeSteps]; + m_kVolumeGM = new GraphicsImage[m_iTimeSteps]; + m_akGradientMagMinMax = new Vector2f[m_iTimeSteps]; + + final int[] aiSubset = new int[] {aiExtents[0], aiExtents[1], aiExtents[2]}; + + for (int i = 0; i < m_iTimeSteps; i++) { +// System.err.println( "initiImages : " + i ); + if ( m_iTimeSteps > 1 ) + { + // Will generate the ModelImage GraphicsImage representation and separate the 4D ModelImage into + // the 3D Subset image. + m_kVolume[i] = initVolumeData(m_kImage, i, m_kVolumeTarget, m_kImage.getImageName(), true, false); + } + else + { + // Already 3D, just generate the GraphicsImage: + m_kVolume[0] = initVolumeData(m_kImage, m_iTimeSlice, m_kVolumeTarget, m_kImage.getImageName(), true, false); + } + + // Allocate GraphcisImage for Gradient Magnitude Texture: + kImageName = ModelImage.makeImageName(m_kImage.getFileInfo(0).getFileName(), + new String("_GM_" + i)); + m_kVolumeGM[i] = new GraphicsImage(type, iXBound, iYBound, iZBound, + (byte[])null, kImageName); + + } + // Initialize the Gradient Magnitude Texture and set its GraphicsImage: + m_kVolumeGMTarget = new Texture(); + m_kVolumeGMTarget.SetImage(m_kVolumeGM[0]); + m_kVolumeGMTarget.SetShared(true); + m_kVolumeGMTarget.SetFilterType(Texture.FilterType.LINEAR); + m_kVolumeGMTarget.SetWrapType(0, Texture.WrapType.CLAMP_BORDER); + m_kVolumeGMTarget.SetWrapType(1, Texture.WrapType.CLAMP_BORDER); + m_kVolumeGMTarget.SetWrapType(2, Texture.WrapType.CLAMP_BORDER); + + // Initialize the ModelImage Texture and set its GraphicsImage: + m_kVolumeTarget = new Texture(); + m_kVolumeTarget.SetImage(m_kVolume[0]); + m_kVolumeTarget.SetShared(true); + m_kVolumeTarget.SetFilterType(Texture.FilterType.LINEAR); + m_kVolumeTarget.SetWrapType(0, Texture.WrapType.CLAMP_BORDER); + m_kVolumeTarget.SetWrapType(1, Texture.WrapType.CLAMP_BORDER); + m_kVolumeTarget.SetWrapType(2, Texture.WrapType.CLAMP_BORDER); + + // Initialize the ColorMap Texture and set its GraphicsImage: + m_kColorMapTarget = new Texture(); + m_kColorMapTarget.SetImage(m_kColorMap); + m_kColorMapTarget.SetShared(true); + + // Initialize the Normal Map Texture and set its GraphicsImage: + m_kScratchTarget = new Texture(); + m_kScratchTarget.SetImage(new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, + (byte[])null, "ScratchBuffer")); + m_kScratchTarget.SetShared(true); + m_kScratchTarget.SetFilterType(Texture.FilterType.LINEAR); + m_kScratchTarget.SetWrapType(0, Texture.WrapType.CLAMP_BORDER); + m_kScratchTarget.SetWrapType(1, Texture.WrapType.CLAMP_BORDER); + m_kScratchTarget.SetWrapType(2, Texture.WrapType.CLAMP_BORDER); + + // Initialize the Opacity Map for the Gradient Magnitude Texture and set its GraphicsImage: + m_kOpacityMapTarget_GM = new Texture(); + m_kOpacityMapTarget_GM.SetImage(m_kOpacityMap_GM); + m_kOpacityMapTarget_GM.SetShared(true); + + // Initialize the Surface Mask Texture and set its GraphicsImage: + m_kSurfaceImage = new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, + new byte[4* iXBound * iYBound * iZBound], "SurfaceImage"); + m_kSurfaceTarget = new Texture(); + m_kSurfaceTarget.SetImage(m_kSurfaceImage); + m_kSurfaceTarget.SetShared(true); + m_kSurfaceTarget.SetFilterType(Texture.FilterType.LINEAR); + m_kSurfaceTarget.SetWrapType(0, Texture.WrapType.CLAMP_BORDER); + m_kSurfaceTarget.SetWrapType(1, Texture.WrapType.CLAMP_BORDER); + m_kSurfaceTarget.SetWrapType(2, Texture.WrapType.CLAMP_BORDER); + + // Calculate the scale factors for rendering the volume with a unit cube: + InitScale(); + } + + + private void initImagesColor() { + m_fDRRNormalize = computeIntegralNormalizationFactor(); + // Initialize Color Map GraphicsImage: + m_kColorMap = initColorMap(); + // Initialize Opacity Map for the GradientMagnitude image: + m_kOpacityMap_GM = InitOpacityMap(m_kImage, new String(m_kPostfix + "_GM")); + + final int iXBound = m_kImage.getExtents()[0]; + final int iYBound = m_kImage.getExtents()[1]; + final int iZBound = m_kImage.getExtents()[2]; + + /* + * Map the ModelImage volume data to a texture image, including for the ModelImage gradient magnitude data: + */ + final int[] aiExtents = m_kImage.getExtents(); + final int iNDims = aiExtents.length; + String kImageName; + GraphicsImage.FormatMode type = GraphicsImage.FormatMode.IT_RGBA8888 ; + + + if (iNDims == 3) { // ModelImage is 3D: + m_iTimeSteps = 1; + } + else { // ModelImage is 4D: + m_iTimeSteps = aiExtents[3]; + } + // A 4D ModelImage is separated into the 3D Volumes: +// m_akImages = new ModelImage[m_iTimeSteps]; + // Allocate a 3D GraphicsImage for each 3D Volume + m_kVolume = new GraphicsImage[m_iTimeSteps]; + m_kVolumeGM = new GraphicsImage[m_iTimeSteps]; + m_kNormal = new GraphicsImage[m_iTimeSteps]; + m_akGradientMagMinMax = new Vector2f[m_iTimeSteps]; + + final int[] aiSubset = new int[] {aiExtents[0], aiExtents[1], aiExtents[2]}; + for (int i = 0; i < m_iTimeSteps; i++) { + + if ( m_iTimeSteps > 1 ) + { + // Will generate the ModelImage GraphicsImage representation and separate the 4D ModelImage into + // the 3D Subset image. +// m_akImages[i] = new ModelImage(m_kImage.getType(), aiSubset, JDialogBase.makeImageName(m_kImage +// .getImageName(), "_" + i)); +// JDialogBase.updateFileInfo( m_kImage, m_akImages[i] ); + m_kVolume[i] = initVolumeData(m_kImage, i, m_kVolumeTarget, m_kImage.getImageName(), true, false); +// m_akImages[i].copyFileTypeInfo(m_kImage); +// m_akImages[i].calcMinMax(); + } + else + { + // Already 3D, just generate the GraphicsImage: +// m_akImages[0] = m_kImage; + m_kVolume[0] = initVolumeData(m_kImage, m_iTimeSlice, m_kVolumeTarget, m_kImage.getImageName(), true, false); + } + // Allocate GraphcisImage for Normal Map Texture: + kImageName = ModelImage.makeImageName(m_kImage.getFileInfo(0).getFileName(), + new String("_Normal_" + i)); + m_kNormal[i] = new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, + (byte[])null, kImageName); + + // Allocate GraphcisImage for Gradient Magnitude Texture: + kImageName = ModelImage.makeImageName(m_kImage.getFileInfo(0).getFileName(), + new String("_GM_" + i)); + m_kVolumeGM[i] = new GraphicsImage(type, iXBound, iYBound, iZBound, + (byte[])null, kImageName); + } + // Initialize the Gradient Magnitude Texture and set its GraphicsImage: + m_kVolumeGMTarget = new Texture(); + m_kVolumeGMTarget.SetImage(m_kVolumeGM[0]); + m_kVolumeGMTarget.SetShared(true); + m_kVolumeGMTarget.SetFilterType(Texture.FilterType.LINEAR); + m_kVolumeGMTarget.SetWrapType(0, Texture.WrapType.CLAMP_BORDER); + m_kVolumeGMTarget.SetWrapType(1, Texture.WrapType.CLAMP_BORDER); + m_kVolumeGMTarget.SetWrapType(2, Texture.WrapType.CLAMP_BORDER); + + // Initialize the ModelImage Texture and set its GraphicsImage: + m_kVolumeTarget = new Texture(); + m_kVolumeTarget.SetImage(m_kVolume[0]); + m_kVolumeTarget.SetShared(true); + m_kVolumeTarget.SetFilterType(Texture.FilterType.LINEAR); + m_kVolumeTarget.SetWrapType(0, Texture.WrapType.CLAMP_BORDER); + m_kVolumeTarget.SetWrapType(1, Texture.WrapType.CLAMP_BORDER); + m_kVolumeTarget.SetWrapType(2, Texture.WrapType.CLAMP_BORDER); + + // Initialize the ColorMap Texture and set its GraphicsImage: + m_kColorMapTarget = new Texture(); + m_kColorMapTarget.SetImage(m_kColorMap); + m_kColorMapTarget.SetShared(true); + + // Initialize the Normal Map Texture and set its GraphicsImage: + m_kNormalMapTarget = new Texture(); + m_kNormalMapTarget.SetImage(m_kNormal[0]); + m_kNormalMapTarget.SetShared(true); + m_kNormalMapTarget.SetFilterType(Texture.FilterType.LINEAR); + m_kNormalMapTarget.SetWrapType(0, Texture.WrapType.CLAMP_BORDER); + m_kNormalMapTarget.SetWrapType(1, Texture.WrapType.CLAMP_BORDER); + m_kNormalMapTarget.SetWrapType(2, Texture.WrapType.CLAMP_BORDER); + + + // Initialize the Normal Map Texture and set its GraphicsImage: + m_kScratchTarget = new Texture(); + m_kScratchTarget.SetImage(new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, + (byte[])null, "ScratchBuffer")); + m_kScratchTarget.SetShared(true); + m_kScratchTarget.SetFilterType(Texture.FilterType.LINEAR); + m_kScratchTarget.SetWrapType(0, Texture.WrapType.CLAMP_BORDER); + m_kScratchTarget.SetWrapType(1, Texture.WrapType.CLAMP_BORDER); + m_kScratchTarget.SetWrapType(2, Texture.WrapType.CLAMP_BORDER); + + // Initialize the Opacity Map for the Gradient Magnitude Texture and set its GraphicsImage: + m_kOpacityMapTarget_GM = new Texture(); + m_kOpacityMapTarget_GM.SetImage(m_kOpacityMap_GM); + m_kOpacityMapTarget_GM.SetShared(true); + + // Initialize the Surface Mask Texture and set its GraphicsImage: + m_kSurfaceImage = new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, + new byte[4* iXBound * iYBound * iZBound], "SurfaceImage"); + m_kSurfaceTarget = new Texture(); + m_kSurfaceTarget.SetImage(m_kSurfaceImage); + m_kSurfaceTarget.SetShared(true); + m_kSurfaceTarget.SetFilterType(Texture.FilterType.LINEAR); + m_kSurfaceTarget.SetWrapType(0, Texture.WrapType.CLAMP_BORDER); + m_kSurfaceTarget.SetWrapType(1, Texture.WrapType.CLAMP_BORDER); + m_kSurfaceTarget.SetWrapType(2, Texture.WrapType.CLAMP_BORDER); + + // Calculate the scale factors for rendering the volume with a unit cube: + InitScale(); + } + + /** + * Create a new LUT for the input image. + * + * @param kImage ModelImage. + */ + private void initLUT() { + + if (m_kImage.isColorImage()) { + final float[] x = new float[4]; + final float[] y = new float[4]; + final Dimension dim = new Dimension(256, 256); + + // Set ModelRGB min max values; + x[0] = 0; + y[0] = dim.height - 1; + + x[1] = 255 * 0.333f; + y[1] = (dim.height - 1) - ( (dim.height - 1) / 3.0f); + + x[2] = 255 * 0.667f; + y[2] = (dim.height - 1) - ( (dim.height - 1) * 0.67f); + + x[3] = 255; + y[3] = 0; + + final int[] RGBExtents = new int[2]; + RGBExtents[0] = 4; + RGBExtents[1] = 256; + m_kRGBT = new ModelRGB(RGBExtents); + m_kRGBT.getRedFunction().importArrays(x, y, 4); + m_kRGBT.getGreenFunction().importArrays(x, y, 4); + m_kRGBT.getBlueFunction().importArrays(x, y, 4); + m_kRGBT.makeRGB( -1); + } else { + final int[] dimExtentsLUT = new int[2]; + + dimExtentsLUT[0] = 4; + dimExtentsLUT[1] = 256; + + m_kLUT = new ModelLUT(ModelLUT.GRAY, 256, dimExtentsLUT); + + float min, max; + + if (m_kImage.getType() == ModelStorageBase.UBYTE) { + min = 0; + max = 255; + } else if (m_kImage.getType() == ModelStorageBase.BYTE) { + min = -128; + max = 127; + } else { + min = (float) m_kImage.getMin(); + max = (float) m_kImage.getMax(); + } + + final float imgMin = (float) m_kImage.getMin(); + final float imgMax = (float) m_kImage.getMax(); + + m_kLUT.resetTransferLine(min, imgMin, max, imgMax); + } + } + + /** + * Initialize the scale factors. Based on the ModelImage Volume. + */ + private void InitScale() { + + int dimX = m_kImage.getExtents().length > 0 ? m_kImage.getExtents()[0] : 1; + int dimY = m_kImage.getExtents().length > 1 ? m_kImage.getExtents()[1] : 1; + int dimZ = m_kImage.getExtents().length > 2 ? m_kImage.getExtents()[2] : 1; + m_iMaxExtent = Math.max( dimX, Math.max( dimY, dimZ ) ); + + final float fMaxX = (m_kImage.getExtents()[0] - 1) * m_kImage.getFileInfo(0).getResolutions()[0]; + final float fMaxY = (m_kImage.getExtents()[1] - 1) * m_kImage.getFileInfo(0).getResolutions()[1]; + final float fMaxZ = (m_kImage.getExtents()[2] - 1) * m_kImage.getFileInfo(0).getResolutions()[2]; + + m_fMax = fMaxX; + if (fMaxY > m_fMax) { + m_fMax = fMaxY; + } + if (fMaxZ > m_fMax) { + m_fMax = fMaxZ; + } + m_fX = fMaxX / m_fMax; + m_fY = fMaxY / m_fMax; + m_fZ = fMaxZ / m_fMax; + } + + /** + * Reads an image from disk. + * + * @param kImageName image name + * @param kDir directory + * @return ModelImage + */ + private static ModelImage ReadFromDisk(final String kImageName, final String kDir) { + + final File kFile = new File(kDir, kImageName); + if ( !kFile.exists()) { + return null; + } + + final FileIO fileIO = new FileIO(); + return fileIO.readImage( kImageName, kDir ); + //return fileIO.readXML(kImageName + ".xml", kDir, false, false); + } + + private void readObject(final java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { + m_kDir = (String) in.readObject(); + if ( !m_kDir.equals("null")) { + final String kImageName = (String) in.readObject(); + m_kPostfix = (String) in.readObject(); + m_kImage = ReadFromDisk(kImageName, m_kDir); + init(null, 0, true); + } + } + + + /** + * Go to the next 3D volume sub-image for the 4D animation. + * Updates the Textures and causes them to be reloaded onto the GPU. + */ + private void update4D() { + if ( m_kImage == null ) return; + + m_kVolumeTarget.SetImage(m_kVolume[m_iTimeSlice]); + m_kVolumeTarget.Reload(true); + if ( m_bGMInit ) + { + if ( m_kVolumeGM[m_iTimeSlice] == null ) + { + ModelImage gmImage = getGradientMagnitude( m_kImage, m_iTimeSlice ); + m_akGradientMagMinMax[m_iTimeSlice] = new Vector2f( (float)gmImage.getMin(), (float)gmImage.getMax() ); + gmImage.disposeLocal(); + m_kVolumeGMTarget.SetImage(createGM_Laplace(gmImage, null, null, 0, true)); + m_kVolumeGMTarget.Reload(true); + } + else + { + m_kVolumeGMTarget.SetImage(m_kVolumeGM[m_iTimeSlice]); + m_kVolumeGMTarget.Reload(true); + } + } + if ( m_bNormalsInit && m_kImage.isColorImage() ) + { + m_kNormalMapTarget.SetImage(m_kNormal[m_iTimeSlice]); + m_kNormalMapTarget.Reload(true); + } + if ( m_bHistoInit && (m_kHisto[m_iTimeSlice] != null )) + { + m_kHistoTarget.SetImage(m_kHisto[m_iTimeSlice]); + m_kHistoTarget.Reload(true); + } + + m_kImage.setTimeSlice(m_iTimeSlice); + } + + /** + * Called when the opacity transfer function changes. This function updates the Texture + * and causes the data to be reloaded onto the GPU. + * + * @param kImage the ModelImage the transfer function applies to. + * @param kOpacityTexture the opacity Texture passed to the GPU + * @param kOpacityMap the opacity data stored in the GraphicsImage + * @param kTransfer the new transfer function. + */ + private boolean UpdateImages(final ModelImage kImage, final Texture kOpacityTexture, + final GraphicsImage kOpacityMap, final TransferFunction kTransfer) { + final int iLutHeight = 256; + final float[] afData = kOpacityMap.GetFloatData(); + + final float fRange = (float) (kImage.getMax() - kImage.getMin()); + final float fStep = fRange / iLutHeight; + float fDataValue = (float) kImage.getMin(); + for (int i = 0; i < iLutHeight; i++) { + afData[i] = (kTransfer.getRemappedValue(fDataValue, iLutHeight) / 255.0f); + fDataValue += fStep; + } + kOpacityTexture.Reload(true); + return true; + } + + public TransferFunction getOpacityFn() { + return opacityTransferFn; + } + + /** + * Update the opacity transfer function. + * + * @param kImage the ModelImage the transfer function applies to. + * @param kOpacityTexture the opacity Texture passed to the GPU + * @param kOpacityMap the opacity data stored in the GraphicsImage + * @param kTransfer the new transfer function. + */ + private boolean UpdateImages2(final ModelImage kImage, final Texture kOpacityTexture, + final GraphicsImage kOpacityMap, final TransferFunction kTransfer) { + opacityTransferFn = new TransferFunction(kTransfer); + final int iLutHeight = kOpacityMap.GetBound(0); + final byte[] abData = kOpacityMap.GetData(); + + final float fRange = (float) (kImage.getMax() - kImage.getMin()); + final float fStep = fRange / iLutHeight; + float fDataValue = (float) kImage.getMin(); + float fVal; + for (int i = 0; i < iLutHeight; i++) { + fVal = (kTransfer.getRemappedValue(fDataValue, iLutHeight) / 255.0f); + abData[i * 4 + 3] = (byte) (fVal * 255); + fDataValue += fStep; + } + kOpacityTexture.Reload(true); + return true; + } + + private void writeObject(final java.io.ObjectOutputStream out) throws IOException { + if (m_kImage != null) { + out.writeObject(m_kDir); + out.writeObject(m_kImage.getImageFileName()); + out.writeObject(m_kPostfix); + m_kImage.saveImage(m_kDir, m_kImage.getImageFileName(), FileUtility.XML, false, false); + } else { + out.writeObject("null"); + } + } +} diff --git a/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java b/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java index d5da71688a..cab640183b 100644 --- a/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java +++ b/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java @@ -61,6 +61,8 @@ import com.jogamp.opengl.awt.GLCanvas; import javax.swing.KeyStroke; +import org.janelia.mipav.test.valueOutput; + import WildMagic.LibFoundation.Mathematics.ColorRGBA; import WildMagic.LibFoundation.Mathematics.Mathf; import WildMagic.LibFoundation.Mathematics.Matrix3f; @@ -857,6 +859,10 @@ private void PickVolume3D(Vector3f kPos, Vector3f kDir, Vector3f maxPt) Vector3f pickedPoints[] = new Vector3f[m_kPicker.Records.size()]; float distances[] = new float[m_kPicker.Records.size()]; + long time = System.currentTimeMillis(); + valueOutput output = new valueOutput("output" + time + ".csv"); + valueOutput outputOld = new valueOutput("outputOld" + time + ".csv"); + for ( int i = 0; i < m_kPicker.Records.size(); i++ ) { PickRecord kPickPoint = m_kPicker.Records.elementAt(i); @@ -880,6 +886,8 @@ private void PickVolume3D(Vector3f kPos, Vector3f kDir, Vector3f maxPt) secondIntersectionPoint.copy(pickedPoints[1]); float maxValue = -Float.MAX_VALUE; + float maxValueOld = -Float.MAX_VALUE; + Vector3f maxPtOld = new Vector3f(); Vector3f p0 = new Vector3f(firstIntersectionPoint); Vector3f p1 = new Vector3f(secondIntersectionPoint); @@ -959,13 +967,39 @@ else if ( (m_kVolumeImageB != null) && (m_kVolumeImageB.GetImage() != null)) { // Only one imageA: value = m_kVolumeImageA.GetTransferedValue(sampleX, sampleY, sampleZ); } + if ( value > maxValueOld ) + { + maxValueOld = value; + maxPtOld.copy(p0); + } + outputOld.writeData(p0.X, p0.Y, p0.Z, value); + + // Old Mipav but more accurate click + value = m_kVolumeImageA.GetImage().getFloatTriLinearBounds(p0.X, p0.Y, p0.Z); + if ( (m_kVolumeImageB != null) && (m_kVolumeImageB.GetImage() != null)) + { + float valueB = m_kVolumeImageB.GetImage().getFloatTriLinearBounds(p0.X, p0.Y, p0.Z); + float blend = getABBlend(); + value = (blend * value + (1 - blend) * valueB); + } + + if ( value > maxValue ) { maxValue = value; maxPt.copy(p0); } + // Write data to CSV + output.writeData(p0.X, p0.Y, p0.Z, value); } + + outputOld.writeData(maxPtOld.X, maxPtOld.Y, maxPtOld.Z, maxValueOld); + output.writeData(maxPt.X, maxPt.Y, maxPt.Z, maxValue); + // Close the output stream + output.close(); + outputOld.close(); + if ( maxValue != -Float.MAX_VALUE ) { boolean picked = false; From 3636593c7e5f66d0d2a7c454ad2485da18e23183 Mon Sep 17 00:00:00 2001 From: chend Date: Tue, 23 Apr 2024 10:11:53 -0400 Subject: [PATCH 02/39] To fix the clicking accuracy issues, the old version(2022) of MIPAV for PickVolum3D were retrieved to replace the current one. An valueOutput class was created for output the data into csv files for plotting. --- .../WildMagic/Render/VolumeImage.java | 29 ++++++++++++ test/org/janelia/mipav/test/valueOutput.java | 44 +++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 test/org/janelia/mipav/test/valueOutput.java diff --git a/src/gov/nih/mipav/view/renderer/WildMagic/Render/VolumeImage.java b/src/gov/nih/mipav/view/renderer/WildMagic/Render/VolumeImage.java index ef09e08086..882bbc8512 100644 --- a/src/gov/nih/mipav/view/renderer/WildMagic/Render/VolumeImage.java +++ b/src/gov/nih/mipav/view/renderer/WildMagic/Render/VolumeImage.java @@ -963,6 +963,35 @@ public float GetTransferedValue( int x, int y, int z ) return TransferValue(value); } + + public ColorRGBA GetTransferedValueOld( float x, float y, float z ) + { + float value = m_kImage.getFloatTriLinearBounds(x, y, z); + + float min = (float) m_kImage.getMin(); + float max = (float) m_kImage.getMax(); + float diff = max - min; + byte index = 0; + if ( (diff > 1) && (diff <= 255) ) + { + index = (byte)(((value - min)/diff) * diff); + } + else + { + index = (byte)(((value - min)/diff) * 255); + } + if ( (index >= 0) && (index < 255) && (m_kColorMap != null) && (m_kColorMap.GetData() != null) ) + { + byte r = m_kColorMap.GetData()[index * 4 + 0]; + byte g = m_kColorMap.GetData()[index * 4 + 1]; + byte b = m_kColorMap.GetData()[index * 4 + 2]; + byte a = m_kColorMap.GetData()[index * 4 + 3]; + return new ColorRGBA(r, g, b, a); + } + return new ColorRGBA(0,0,0,0); + } + + private float TransferValue(float value) { float min = (float) m_kImage.getMin(); float max = (float) m_kImage.getMax(); diff --git a/test/org/janelia/mipav/test/valueOutput.java b/test/org/janelia/mipav/test/valueOutput.java new file mode 100644 index 0000000000..545a14bb43 --- /dev/null +++ b/test/org/janelia/mipav/test/valueOutput.java @@ -0,0 +1,44 @@ +package org.janelia.mipav.test; + + +import java.io.FileWriter; +import java.io.IOException; + +public class valueOutput { + private FileWriter fileWriter; + + public valueOutput(String fileName) { + try { + this.fileWriter = new FileWriter(fileName); + // Writing the header + this.fileWriter.append("p0.X,p0.Y,p0.Z,value\n"); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + + public void writeData(float x, float y, float z, float value) { + // Writing data in the CSV format + try { + this.fileWriter.append(String.format("%.1f,%.1f,%.1f,%.1f\n", x, y, z, value)); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + public void close() { + // Closing the FileWriter + try { + this.fileWriter.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } +} + + + From d75f20f950ca0c48b62099f31def2eb2b78d9cb8 Mon Sep 17 00:00:00 2001 From: chend Date: Tue, 23 Apr 2024 13:30:54 -0400 Subject: [PATCH 03/39] Switched the mis-named variables. --- .gitignore | 6 ++- .../WildMagic/VolumeTriPlanarRender.java | 42 +++++++++---------- .../{valueOutput.java => ValueOutput.java} | 4 +- 3 files changed, 28 insertions(+), 24 deletions(-) rename test/org/janelia/mipav/test/{valueOutput.java => ValueOutput.java} (92%) diff --git a/.gitignore b/.gitignore index 21ed2319f0..8ed7b69bbe 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,8 @@ /bin/ /export/ /null/ -classes \ No newline at end of file +classes +*.csv +*.pdf +*.png +/output/ diff --git a/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java b/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java index cab640183b..f3d6086f9a 100644 --- a/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java +++ b/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java @@ -61,7 +61,7 @@ import com.jogamp.opengl.awt.GLCanvas; import javax.swing.KeyStroke; -import org.janelia.mipav.test.valueOutput; +import org.janelia.mipav.test.ValueOutput; import WildMagic.LibFoundation.Mathematics.ColorRGBA; import WildMagic.LibFoundation.Mathematics.Mathf; @@ -846,7 +846,7 @@ else if ( m_bErase ) m_bMouseDrag = false; } - private void PickVolume3D(Vector3f kPos, Vector3f kDir, Vector3f maxPt) + private void PickVolume3D(Vector3f kPos, Vector3f kDir, Vector3f maxPtAccurate) { m_kPicker.Execute(m_kVolumeRayCast.GetScene(),kPos,kDir,0.0f, Float.MAX_VALUE); @@ -860,8 +860,8 @@ private void PickVolume3D(Vector3f kPos, Vector3f kDir, Vector3f maxPt) float distances[] = new float[m_kPicker.Records.size()]; long time = System.currentTimeMillis(); - valueOutput output = new valueOutput("output" + time + ".csv"); - valueOutput outputOld = new valueOutput("outputOld" + time + ".csv"); + ValueOutput outputAccurate = new ValueOutput("outputAccurate" + time + ".csv"); + ValueOutput output = new ValueOutput("output" + time + ".csv"); for ( int i = 0; i < m_kPicker.Records.size(); i++ ) { @@ -885,9 +885,9 @@ private void PickVolume3D(Vector3f kPos, Vector3f kDir, Vector3f maxPt) firstIntersectionPoint.copy(pickedPoints[0]); secondIntersectionPoint.copy(pickedPoints[1]); + float maxValueAccurate = -Float.MAX_VALUE; float maxValue = -Float.MAX_VALUE; - float maxValueOld = -Float.MAX_VALUE; - Vector3f maxPtOld = new Vector3f(); + Vector3f maxPt = new Vector3f(); Vector3f p0 = new Vector3f(firstIntersectionPoint); Vector3f p1 = new Vector3f(secondIntersectionPoint); @@ -967,12 +967,12 @@ else if ( (m_kVolumeImageB != null) && (m_kVolumeImageB.GetImage() != null)) { // Only one imageA: value = m_kVolumeImageA.GetTransferedValue(sampleX, sampleY, sampleZ); } - if ( value > maxValueOld ) + if ( value > maxValue ) { - maxValueOld = value; - maxPtOld.copy(p0); + maxValue = value; + maxPt.copy(p0); } - outputOld.writeData(p0.X, p0.Y, p0.Z, value); + output.writeData(p0.X, p0.Y, p0.Z, value); // Old Mipav but more accurate click value = m_kVolumeImageA.GetImage().getFloatTriLinearBounds(p0.X, p0.Y, p0.Z); @@ -984,33 +984,33 @@ else if ( (m_kVolumeImageB != null) && (m_kVolumeImageB.GetImage() != null)) { } - if ( value > maxValue ) + if ( value > maxValueAccurate ) { - maxValue = value; - maxPt.copy(p0); + maxValueAccurate = value; + maxPtAccurate.copy(p0); } // Write data to CSV - output.writeData(p0.X, p0.Y, p0.Z, value); + outputAccurate.writeData(p0.X, p0.Y, p0.Z, value); } - outputOld.writeData(maxPtOld.X, maxPtOld.Y, maxPtOld.Z, maxValueOld); output.writeData(maxPt.X, maxPt.Y, maxPt.Z, maxValue); + outputAccurate.writeData(maxPtAccurate.X, maxPtAccurate.Y, maxPtAccurate.Z, maxValueAccurate); // Close the output stream + outputAccurate.close(); output.close(); - outputOld.close(); - if ( maxValue != -Float.MAX_VALUE ) + if ( maxValueAccurate != -Float.MAX_VALUE ) { boolean picked = false; // System.err.println( "mouse drag? " + m_bMouseDrag ); if ( !m_bMouseDrag ) { // select or create a new marker: - picked = select3DMarker( firstIntersectionPoint, secondIntersectionPoint, maxPt, rightMousePressed, altPressed ); + picked = select3DMarker( firstIntersectionPoint, secondIntersectionPoint, maxPtAccurate, rightMousePressed, altPressed ); } else if ( m_bMouseDrag ) { // modify currently selected, if exists - picked = modify3DMarker( firstIntersectionPoint, secondIntersectionPoint, maxPt ); + picked = modify3DMarker( firstIntersectionPoint, secondIntersectionPoint, maxPtAccurate ); } if ( !picked ) { @@ -1019,8 +1019,8 @@ else if ( m_bMouseDrag ) { int colorID = 0; VOI newTextVOI = new VOI((short) colorID, "annotation3d_" + id, VOI.ANNOTATION, -1.0f); VOIText textVOI = new VOIText( ); - textVOI.add( maxPt ); - textVOI.add( maxPt ); + textVOI.add( maxPtAccurate ); + textVOI.add( maxPtAccurate ); // Transformation world = m_kVolumeRayCast.getMesh().World; // Vector3f dir = world.InvertVector(m_spkCamera.GetRVector()); // dir.scale(5); diff --git a/test/org/janelia/mipav/test/valueOutput.java b/test/org/janelia/mipav/test/ValueOutput.java similarity index 92% rename from test/org/janelia/mipav/test/valueOutput.java rename to test/org/janelia/mipav/test/ValueOutput.java index 545a14bb43..aadf11a6b6 100644 --- a/test/org/janelia/mipav/test/valueOutput.java +++ b/test/org/janelia/mipav/test/ValueOutput.java @@ -4,10 +4,10 @@ import java.io.FileWriter; import java.io.IOException; -public class valueOutput { +public class ValueOutput { private FileWriter fileWriter; - public valueOutput(String fileName) { + public ValueOutput(String fileName) { try { this.fileWriter = new FileWriter(fileName); // Writing the header From e816823b3b093a7484d9f663ebafb172a02a1e7d Mon Sep 17 00:00:00 2001 From: chend Date: Wed, 1 May 2024 17:57:24 -0400 Subject: [PATCH 04/39] Switch between 2 modes(Accurate vs 3-color mode) by press key "M" or "m".toggleAccurateMode() and isAccurateMode() is created. --- .../VOI/VOILatticeManagerInterface.java | 60 ++++++--- .../WildMagic/VolumeTriPlanarRender.java | 127 +++++++++--------- 2 files changed, 104 insertions(+), 83 deletions(-) diff --git a/src/gov/nih/mipav/view/renderer/WildMagic/VOI/VOILatticeManagerInterface.java b/src/gov/nih/mipav/view/renderer/WildMagic/VOI/VOILatticeManagerInterface.java index bcca3ceb42..ee40c9b61a 100644 --- a/src/gov/nih/mipav/view/renderer/WildMagic/VOI/VOILatticeManagerInterface.java +++ b/src/gov/nih/mipav/view/renderer/WildMagic/VOI/VOILatticeManagerInterface.java @@ -1120,11 +1120,33 @@ public void keyPressed(KeyEvent e) { } + + private boolean accurateMode = true; + + public boolean isAccurateMode() { + return accurateMode; + } + + public void toggleAccurateMode() { + accurateMode = !accurateMode; + System.out.println("Toggle Accurate Mode: " + accurateMode); + } + + + public void keyReleased(KeyEvent e) { isShiftSelected = e.isShiftDown(); movingPickedPoint = false; - if(editingCrossSections) { - switch(e.getKeyChar()) { + + System.out.println(e.getKeyChar()); + + if (e.getKeyCode() == KeyEvent.VK_M) { + toggleAccurateMode(); + System.out.println("Mode changed. Accurate mode is now " + (isAccurateMode() ? "enabled" : "disabled")); + } + + if (editingCrossSections) { + switch (e.getKeyChar()) { case '+': latticeModel.decreaseCrossSectionSamples(); break; @@ -1155,7 +1177,7 @@ public void keyReleased(KeyEvent e) { case 'R': latticeModel.showLattice(false); latticeModel.resetCrossSections(); - if(editingCrossSections) { + if (editingCrossSections) { latticeModel.showLattice(true); } break; @@ -1168,46 +1190,40 @@ public void keyReleased(KeyEvent e) { } } } - - public static VOIBase findNearestAnnotation( final VOI annotations, final Vector3f startPt, final Vector3f endPt, final Vector3f pt ) { + public static VOIBase findNearestAnnotation(final VOI annotations, final Vector3f startPt, final Vector3f endPt, + final Vector3f pt) { int pickedAnnotation = -1; float minDist = Float.MAX_VALUE; - for ( int i = 0; i < annotations.getCurves().size(); i++ ) - { + for (int i = 0; i < annotations.getCurves().size(); i++) { final Vector3f annotationPt = annotations.getCurves().elementAt(i).elementAt(0); final float distance = pt.distance(annotationPt); - if ( distance < minDist ) - { + if (distance < minDist) { minDist = distance; - if ( minDist <= 12 ) - { + if (minDist <= 12) { pickedAnnotation = i; } } } // System.err.println("findNearestAnnotation " + minDist + " " + pickedAnnotation ); - if ( (pickedAnnotation == -1) && (startPt != null) && (endPt != null) ) - { + if ((pickedAnnotation == -1) && (startPt != null) && (endPt != null)) { minDist = Float.MAX_VALUE; // look at the vector under the mouse and see which lattice point is closest... final Segment3f mouseVector = new Segment3f(startPt, endPt); - for ( int i = 0; i < annotations.getCurves().size(); i++ ) - { - DistanceVector3Segment3 dist = new DistanceVector3Segment3(annotations.getCurves().elementAt(i).elementAt(0), mouseVector); + for (int i = 0; i < annotations.getCurves().size(); i++) { + DistanceVector3Segment3 dist = new DistanceVector3Segment3( + annotations.getCurves().elementAt(i).elementAt(0), mouseVector); float distance = dist.Get(); - // System.err.println( i + " " + distance ); - if ( distance < minDist ) - { + // System.err.println( i + " " + distance ); + if (distance < minDist) { minDist = distance; - if ( minDist <= 12 ) - { + if (minDist <= 12) { pickedAnnotation = i; } } } } - if ( pickedAnnotation != -1 ) { + if (pickedAnnotation != -1) { return annotations.getCurves().elementAt(pickedAnnotation); } return null; diff --git a/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java b/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java index f3d6086f9a..e4857baed1 100644 --- a/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java +++ b/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java @@ -939,80 +939,85 @@ private void PickVolume3D(Vector3f kPos, Vector3f kDir, Vector3f maxPtAccurate) continue; } } - float value = 0; - int sampleX = Math.round(p0.X); - int sampleY = Math.round(p0.Y); - int sampleZ = Math.round(p0.Z); - // loop over imageA, imageB, hyperstack, get value, apply color & opacity for 'value' - // take the maximum value: - if ( hyperstack != null ) { - for ( int h = 0; h < hyperstack.length; h++ ) { - if ( (hyperstack[h] != null) && (hyperstack[h].GetImage() != null) ) - { - if ( m_kVolumeRayCast.getImageOn(h) ) - { - value = Math.max(value, hyperstack[h].GetTransferedValue(sampleX, sampleY, sampleZ)); + + + // Two modes can be switched from the accurate mode to the 3-color mode(not working currently) by press key "M" or "m" + if (m_kVOIInterface.isAccurateMode()) { + // Old Mipav but more accurate click + float valueAccurate = 0; + valueAccurate = m_kVolumeImageA.GetImage().getFloatTriLinearBounds(p0.X, p0.Y, p0.Z); + if ((m_kVolumeImageB != null) && (m_kVolumeImageB.GetImage() != null)) { + float valueB = m_kVolumeImageB.GetImage().getFloatTriLinearBounds(p0.X, p0.Y, p0.Z); + float blend = getABBlend(); + valueAccurate = (blend * valueAccurate + (1 - blend) * valueB); + } + + if (valueAccurate > maxValueAccurate) { + maxValueAccurate = valueAccurate; + maxPtAccurate.copy(p0); + } + // Write data to CSV + outputAccurate.writeData(p0.X, p0.Y, p0.Z, valueAccurate); + + } else { + //this is not working, made for more than 3 colors. -Diyi Chen May 1, 2024 + float value = 0; + int sampleX = Math.round(p0.X); + int sampleY = Math.round(p0.Y); + int sampleZ = Math.round(p0.Z); + // loop over imageA, imageB, hyperstack, get value, apply color & opacity for + // 'value' + // take the maximum value: + if (hyperstack != null) { + for (int h = 0; h < hyperstack.length; h++) { + if ((hyperstack[h] != null) && (hyperstack[h].GetImage() != null)) { + if (m_kVolumeRayCast.getImageOn(h)) { + value = Math.max(value, + hyperstack[h].GetTransferedValue(sampleX, sampleY, sampleZ)); + } } } + } else if ((m_kVolumeImageB != null) && (m_kVolumeImageB.GetImage() != null)) { + float valueA = m_kVolumeImageA.GetTransferedValue(sampleX, sampleY, sampleZ); + float valueB = m_kVolumeImageB.GetTransferedValue(sampleX, sampleY, sampleZ); + float blend = getABBlend(); + value = (blend * valueA + (1 - blend) * valueB); + } else { + // Only one imageA: + value = m_kVolumeImageA.GetTransferedValue(sampleX, sampleY, sampleZ); } + if (value > maxValue) { + maxValue = value; + maxPt.copy(p0); + } + output.writeData(p0.X, p0.Y, p0.Z, value); } - else if ( (m_kVolumeImageB != null) && (m_kVolumeImageB.GetImage() != null)) { - float valueA = m_kVolumeImageA.GetTransferedValue(sampleX, sampleY, sampleZ); - float valueB = m_kVolumeImageB.GetTransferedValue(sampleX, sampleY, sampleZ); - float blend = getABBlend(); - value = (blend * valueA + (1 - blend) * valueB); - } - else - { - // Only one imageA: - value = m_kVolumeImageA.GetTransferedValue(sampleX, sampleY, sampleZ); - } - if ( value > maxValue ) - { - maxValue = value; - maxPt.copy(p0); - } - output.writeData(p0.X, p0.Y, p0.Z, value); - - // Old Mipav but more accurate click - value = m_kVolumeImageA.GetImage().getFloatTriLinearBounds(p0.X, p0.Y, p0.Z); - if ( (m_kVolumeImageB != null) && (m_kVolumeImageB.GetImage() != null)) - { - float valueB = m_kVolumeImageB.GetImage().getFloatTriLinearBounds(p0.X, p0.Y, p0.Z); - float blend = getABBlend(); - value = (blend * value + (1 - blend) * valueB); - } - - - if ( value > maxValueAccurate ) - { - maxValueAccurate = value; - maxPtAccurate.copy(p0); - } - // Write data to CSV - outputAccurate.writeData(p0.X, p0.Y, p0.Z, value); } output.writeData(maxPt.X, maxPt.Y, maxPt.Z, maxValue); outputAccurate.writeData(maxPtAccurate.X, maxPtAccurate.Y, maxPtAccurate.Z, maxValueAccurate); + + // Close the output stream + outputAccurate.close(); + output.close(); - // Close the output stream - outputAccurate.close(); - output.close(); - - if ( maxValueAccurate != -Float.MAX_VALUE ) - { + if (!m_kVOIInterface.isAccurateMode()) { + maxValueAccurate = maxValue; + maxPtAccurate.copy(maxPt); + } + + if (maxValueAccurate != -Float.MAX_VALUE) { boolean picked = false; // System.err.println( "mouse drag? " + m_bMouseDrag ); - if ( !m_bMouseDrag ) { + if (!m_bMouseDrag) { // select or create a new marker: - picked = select3DMarker( firstIntersectionPoint, secondIntersectionPoint, maxPtAccurate, rightMousePressed, altPressed ); - } - else if ( m_bMouseDrag ) { + picked = select3DMarker(firstIntersectionPoint, secondIntersectionPoint, maxPtAccurate, + rightMousePressed, altPressed); + } else if (m_bMouseDrag) { // modify currently selected, if exists - picked = modify3DMarker( firstIntersectionPoint, secondIntersectionPoint, maxPtAccurate ); + picked = modify3DMarker(firstIntersectionPoint, secondIntersectionPoint, maxPtAccurate); } - if ( !picked ) + if (!picked) { // add a new picked point: short id = (short) m_kVolumeImageA.GetImage().getVOIs().getUniqueID(); @@ -1052,7 +1057,7 @@ else if ( !doAutomaticLabels() ) } } } - } +} private boolean PickSlice3D(Vector3f kPos, Vector3f kDir, Vector3f maxPt) { From caf1a7d348c14e92b159f0e541b0954dd2039887 Mon Sep 17 00:00:00 2001 From: chend Date: Mon, 13 May 2024 09:43:52 -0400 Subject: [PATCH 05/39] An accurateModeButton added for switch modes between accurate clicking vs 3 color mode(inaccurate clicking) with its actionListener --- .../PlugInDialogVolumeRenderDualJanelia.java | 47 ++++++++++++++++++- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java index c7e96f2ad1..1f3ff4927d 100644 --- a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java +++ b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java @@ -111,6 +111,7 @@ import javax.swing.JSplitPane; import javax.swing.JTabbedPane; import javax.swing.JTextField; +import javax.swing.JToggleButton; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; @@ -135,7 +136,7 @@ * processes. Provides framework for animating the annotations after untwisting. */ public class PlugInDialogVolumeRenderDualJanelia extends JFrame implements ActionListener, RendererListener, - PropertyChangeListener, ViewImageUpdateInterface, WindowListener, ChangeListener { + PropertyChangeListener, ViewImageUpdateInterface, WindowListener, ChangeListener, AccurateModeListener { private static final long serialVersionUID = -9056581285643263551L; @@ -215,6 +216,9 @@ public class PlugInDialogVolumeRenderDualJanelia extends JFrame implements Actio private JPanel opacityPanel; private JTabbedPane opacityTab; private JPanel clipPanel; + private JPanel accuratePanel; + //private JButton accurateModeButton; + private JToggleButton accurateModeButton; private PlugInDialogVolumeRenderDualJanelia parent; private JTextField rangeFusionText; @@ -1132,9 +1136,11 @@ public void enableNext(int mode) { lutPanel.removeAll(); opacityPanel.removeAll(); clipPanel.removeAll(); + accuratePanel.removeAll(); tabbedPane.addTab("LUT", null, lutPanel); tabbedPane.addTab("Opacity", null, opacityPanel); tabbedPane.addTab("Clip", null, clipPanel); + tabbedPane.addTab("AccurateMode", null, accuratePanel); tabbedPane.addChangeListener(this); startButton.setEnabled(true); @@ -1318,6 +1324,13 @@ public boolean updateImages(ModelLUT LUTa, ModelLUT LUTb, boolean flag, int inte // } return false; } + + + @Override + public void accurateModeChanged(boolean isAccurateMode) { + accurateModeButton.setSelected(isAccurateMode); + accurateModeButton.setText(isAccurateMode ? "Accurate Mode is now On" : "Accurate Mode is now Off"); + } @Override public void windowActivated(WindowEvent e) { @@ -1714,6 +1727,7 @@ private boolean openHyperStack() { leftImage.volumeImage = leftImage.hyperstack[0]; //new VolumeImage(false, leftImage.wormImage, "A", null, 0, false); leftImage.voiManager = new VOILatticeManagerInterface(null, leftImage.volumeImage.GetImage(), null, 0, true, null); + leftImage.voiManager.addAccurateModeListener(this); byte[] aucData = new byte[256 * 4 * images.length]; GraphicsImage cmImage = new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, 256, images.length, aucData, new String("colormap" + leftImage.volumeImage.GetImage().getImageFileName())); @@ -2263,12 +2277,40 @@ private void init() { opacityTab = new JTabbedPane(); opacityTab.addChangeListener(this); opacityPanel.add(opacityTab, BorderLayout.CENTER); - clipPanel = new JPanel( new BorderLayout() ); + + //added a panel with button to be able to turn off accurate mode and switch to 3-color mode(not working currently) + accuratePanel = new JPanel(new BorderLayout()); + JPanel buttonPanel = new JPanel(); + //accurateModeButton = new JButton("Accurate Mode"); + accurateModeButton = new JToggleButton("Accurate Mode"); + accurateModeChanged(true); + accurateModeButton.setPreferredSize(new Dimension(200, 50)); + + + // Add an action response to the button + accurateModeButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + // activeImage.voiManager.toggleAccurateMode(); + + JToggleButton toggleButton = (JToggleButton) e.getSource(); + boolean isSelected = toggleButton.isSelected(); + activeImage.voiManager.toggleAccurateMode(); + } + + }); + + // Add the accurateModeButton to the buttonPanel, then add the buttonPanel to the accuratePanel + buttonPanel.add(accurateModeButton); + accuratePanel.add(buttonPanel, BorderLayout.CENTER); + + tabbedPane = new JTabbedPane(); tabbedPane.addTab("LUT", null, lutPanel); tabbedPane.addTab("Opacity", null, opacityPanel); tabbedPane.addTab("Clip", null, clipPanel); + tabbedPane.addTab("AccurateMode", null, accuratePanel); tabbedPane.setVisible(false); tabbedPane.addChangeListener(this); @@ -2279,6 +2321,7 @@ private void init() { imageChannels = new JPanel(); imageChannels.add(new JLabel("Select image channel:") ); + imageChannels.add(new JLabel("Select Mode:")); imageChannels.setVisible(false); leftPanel.add(imageChannels, BorderLayout.CENTER); leftPanel.add(tabbedPane, BorderLayout.SOUTH); From 6709dd2365587120aeee23eda30e20832de83595 Mon Sep 17 00:00:00 2001 From: chend Date: Mon, 13 May 2024 09:46:07 -0400 Subject: [PATCH 06/39] An accurateModeButton added for switch the mode between accurate mode vs 3 color mode (inaccurate currently) with its actionListener. --- .../worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java index 1f3ff4927d..404715c46c 100644 --- a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java +++ b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java @@ -2298,7 +2298,6 @@ public void actionPerformed(ActionEvent e) { boolean isSelected = toggleButton.isSelected(); activeImage.voiManager.toggleAccurateMode(); } - }); // Add the accurateModeButton to the buttonPanel, then add the buttonPanel to the accuratePanel From b5fc4a2f5eabb9d867dc2135daf0a478fee12a89 Mon Sep 17 00:00:00 2001 From: chend Date: Mon, 13 May 2024 09:47:15 -0400 Subject: [PATCH 07/39] An keyBoard "M" added for switch the mode between accurate mode vs 3 color mode (inaccurate currently). --- .../VOI/VOILatticeManagerInterface.java | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/gov/nih/mipav/view/renderer/WildMagic/VOI/VOILatticeManagerInterface.java b/src/gov/nih/mipav/view/renderer/WildMagic/VOI/VOILatticeManagerInterface.java index ee40c9b61a..58201fe4f2 100644 --- a/src/gov/nih/mipav/view/renderer/WildMagic/VOI/VOILatticeManagerInterface.java +++ b/src/gov/nih/mipav/view/renderer/WildMagic/VOI/VOILatticeManagerInterface.java @@ -23,6 +23,8 @@ import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.io.File; +import java.util.ArrayList; +import java.util.List; import java.util.Vector; import javax.swing.ButtonGroup; @@ -33,6 +35,8 @@ import javax.swing.JPanel; import javax.swing.JTextField; +import org.janelia.mipav.plugins.worm.untwisting.AccurateModeListener; + import WildMagic.LibFoundation.Distance.DistanceVector3Segment3; import WildMagic.LibFoundation.Mathematics.Segment3f; import WildMagic.LibFoundation.Mathematics.Vector3f; @@ -1122,6 +1126,8 @@ public void keyPressed(KeyEvent e) { private boolean accurateMode = true; + // TODO: might want to change the array into set + private List listeners = new ArrayList<>(); public boolean isAccurateMode() { return accurateMode; @@ -1129,10 +1135,15 @@ public boolean isAccurateMode() { public void toggleAccurateMode() { accurateMode = !accurateMode; - System.out.println("Toggle Accurate Mode: " + accurateMode); + for (AccurateModeListener listener : listeners) { + listener.accurateModeChanged(accurateMode); + + } + } + + public void addAccurateModeListener(AccurateModeListener listener) { + listeners.add(listener); } - - public void keyReleased(KeyEvent e) { isShiftSelected = e.isShiftDown(); @@ -1142,7 +1153,6 @@ public void keyReleased(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_M) { toggleAccurateMode(); - System.out.println("Mode changed. Accurate mode is now " + (isAccurateMode() ? "enabled" : "disabled")); } if (editingCrossSections) { From c014917b5bd013a9ef5d5f6b56f7e692d9844876 Mon Sep 17 00:00:00 2001 From: chend Date: Mon, 13 May 2024 10:49:48 -0400 Subject: [PATCH 08/39] An AccurateModeListener interface was made for responding to the accurateModeListener --- .../plugins/worm/untwisting/AccurateModeListener.java | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/org/janelia/mipav/plugins/worm/untwisting/AccurateModeListener.java diff --git a/src/org/janelia/mipav/plugins/worm/untwisting/AccurateModeListener.java b/src/org/janelia/mipav/plugins/worm/untwisting/AccurateModeListener.java new file mode 100644 index 0000000000..2b59754ceb --- /dev/null +++ b/src/org/janelia/mipav/plugins/worm/untwisting/AccurateModeListener.java @@ -0,0 +1,7 @@ +package org.janelia.mipav.plugins.worm.untwisting; + + +public interface AccurateModeListener { + // Add this method for responding to the accurateModeListener + void accurateModeChanged(boolean isAccurateMode); +} \ No newline at end of file From 18d9831b8c03b9d3ea8042757ad208ea8003750a Mon Sep 17 00:00:00 2001 From: chend Date: Mon, 13 May 2024 13:22:31 -0400 Subject: [PATCH 09/39] Changed the tabbedPan name into "Selection" --- .../worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java index 404715c46c..7b45b43a02 100644 --- a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java +++ b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java @@ -2309,7 +2309,7 @@ public void actionPerformed(ActionEvent e) { tabbedPane.addTab("LUT", null, lutPanel); tabbedPane.addTab("Opacity", null, opacityPanel); tabbedPane.addTab("Clip", null, clipPanel); - tabbedPane.addTab("AccurateMode", null, accuratePanel); + tabbedPane.addTab("Selection", null, accuratePanel); tabbedPane.setVisible(false); tabbedPane.addChangeListener(this); From 39c46e3564cf3bc5e80e1ffbd9b797d52547351a Mon Sep 17 00:00:00 2001 From: Mark Kittisopikul Date: Mon, 13 May 2024 13:57:54 -0400 Subject: [PATCH 10/39] Restore VolumeImage.java --- .../WildMagic/Render/VolumeImage.java | 4813 ++++++++--------- 1 file changed, 2391 insertions(+), 2422 deletions(-) diff --git a/src/gov/nih/mipav/view/renderer/WildMagic/Render/VolumeImage.java b/src/gov/nih/mipav/view/renderer/WildMagic/Render/VolumeImage.java index 882bbc8512..704026790d 100644 --- a/src/gov/nih/mipav/view/renderer/WildMagic/Render/VolumeImage.java +++ b/src/gov/nih/mipav/view/renderer/WildMagic/Render/VolumeImage.java @@ -1,2422 +1,2391 @@ -package gov.nih.mipav.view.renderer.WildMagic.Render; - - -import static java.lang.System.nanoTime; -import static java.lang.System.out; -import gov.nih.mipav.model.algorithms.filters.AlgorithmGradientMagnitudeSep; -import gov.nih.mipav.model.algorithms.filters.OpenCL.filters.OpenCLAlgorithmGradientMagnitude; -import gov.nih.mipav.model.algorithms.filters.OpenCL.filters.OpenCLAlgorithmVolumeNormals; -import gov.nih.mipav.model.file.*; -import gov.nih.mipav.model.structures.*; - -import gov.nih.mipav.view.*; -import gov.nih.mipav.view.dialogs.*; -import gov.nih.mipav.view.renderer.WildMagic.VolumeTriPlanarInterface; - -import java.awt.Dimension; -import java.awt.event.ActionEvent; -import java.io.*; -import java.nio.Buffer; -import java.util.BitSet; -import java.util.Vector; - - -import org.jocl.CL; - -import WildMagic.LibFoundation.Mathematics.ColorRGB; -import WildMagic.LibFoundation.Mathematics.ColorRGBA; -import WildMagic.LibFoundation.Mathematics.Vector2f; -import WildMagic.LibGraphics.Rendering.*; - -/** * - * The VolumeImage class provides an interface between the MIPAV ModelImage and the 2D and 3D Textures used to render - * the ModelImage on the GPU. The VolumeImage creates the supporting Texture and GraphicImage objects that pass - * the ModelImage data to the GPU. It also creates Texture and GraphicsImage objects for the ModelImage Look-up Table (LUT) - * and the ModelImage opacity transfer function. Other textures that are used for advanced volume rendering, such as - * a normal map for surface rendering, and the gradient-magnitude and laplace images for the multi-histogram rendering are - * calculated and passed on-demand to the GPU when the user selects these options in the Volume-Renderer user-interface. - * - * The VolumeImage data structure handles all GPU content for rendering one ModelImage. All Textures and GraphicsImages - * are initialized and stored in the VolumeImage. When needed by the renderer they are loaded onto the GPU. Any images - * that are derived from the ModelImage: Normal map, Gradient Magnitude, Laplace, are either read from file or calculated - * and then written to file. The supporting files are stored in a directory on disk located next to the original ModelImage. - * The directory is named with the ModelImage name followed by "_RenderFiles". - * - * - */ -public class VolumeImage implements Serializable { - /** */ - private static final long serialVersionUID = -7254697711265907746L; - - /** Reference to ModelImage image */ - private ModelImage m_kImage; - private ModelImage m_kImageGM; - - /** GraphicsImage contains GM opacity transfer function data: */ - private GraphicsImage m_kOpacityMap_GM = null; - - /** - * Texture contains texture filter modes and GraphicsImage for opacity transfer function: - */ - private Texture m_kOpacityMapTarget_GM = null; - - /** Data storage for volume: */ - private GraphicsImage[] m_kVolume; - - /** Texture object for data: */ - private Texture m_kVolumeTarget; - - /** Data storage for normals: */ - private GraphicsImage[] m_kNormal; - /** Set to true if the Normal Map has been initialized. */ - private boolean m_bNormalsInit = false; - - /** Texture object for normal map: */ - private Texture m_kNormalMapTarget; - - /** Texture object for GPU computations: */ - private Texture m_kScratchTarget; - - /** Data storage for color map: */ - private GraphicsImage m_kColorMap; - - /** Texture object for color map: */ - private Texture m_kColorMapTarget; - - /** Data storage for volume gradient magnitude: */ - private GraphicsImage[] m_kVolumeGM; - /** Set to true if the Gradient Magnitude texture map has been initialized. */ - private boolean m_bGMInit = false; - - /** Texture object for volume gradient magnitude data: */ - private Texture m_kVolumeGMTarget; - - /** Data storage for surfaces: */ - private GraphicsImage m_kSurfaceImage; - - /** Texture object for surfaces: */ - private Texture m_kSurfaceTarget; - - /** ModelLUT */ - private ModelLUT m_kLUT = null; - - /** ModelRGB */ - private ModelRGB m_kRGBT = null; - - /** Image scale factors for display in 3D */ - private float m_fX = 1, m_fY = 1, m_fZ = 1, m_fMax = 1; - private int m_iMaxExtent = 1; - - /** Image name post-fix typically either 'A' or 'B' */ - private String m_kPostfix = null; - - /** Directory for calculated images */ - private String m_kDir = null; - - /** Histogram data for multi-histogram interface */ - private GraphicsImage[] m_kHisto = null; - /** Set to true when the multi-histogram histogram texture has been initialized. */ - private boolean m_bHistoInit = false; - - /** Texture object for data: */ - private Texture m_kHistoTarget; - - /** Texture coordinates for displaying histogram in 2D */ - private Vector2f[] m_akHistoTCoord = null; - - private float m_fDRRNormalize = 255.0f; - - /** Current position in time (4D data) */ - private int m_iTimeSlice = 0; - - /** Total number of time-slices (4D data) */ - private int m_iTimeSteps = 0; - - /** 3D sub-images (4D data) */ -// private ModelImage[] m_akImages; -// private ModelImage[] m_akImagesGM; - - private Vector2f[] m_akGradientMagMinMax; - - private TransferFunction opacityTransferFn; - - /* Default Constructor */ - public VolumeImage() {} - - /** - * Create a Volume image with the input ModelImage. The supporting images for advanced volume rendering, such as - * the normal map, gradient magnitude and laplace images are generated on-demand and stored in a directory for - * later use. The directory is created if it does not already exist, with the ModelImage name + "_RenderFiles" as - * the directory name. - * - * @param bClone, when true clone the input ModelImage, when false reference the ModelImage - * @param kImage input ModelImage - * @param kPostfix Postfix for images 'A' or 'B' - * @param kProgress progress bar - * @param iProgress progress bar increment - */ - public VolumeImage(boolean bClone, final ModelImage kImage, final String kPostfix, final ViewJProgressBar kProgress, final int iProgress) { - this( bClone, kImage, kPostfix, kProgress, iProgress, true ); - } - - public VolumeImage(boolean bClone, final ModelImage kImage, final String kPostfix, final ViewJProgressBar kProgress, final int iProgress, boolean initGradientMagnitude) { - m_kPostfix = new String(kPostfix); - // clone the input image, in the future this might be a reference. - if ( bClone ) - { - m_kImage = (ModelImage)kImage.clone(); - } - else - { - m_kImage = kImage; - } - // Initialize the Texture maps. - init(kProgress, iProgress, initGradientMagnitude); - } - - /** - * Copy the data from the input GraphicsImage and return a new ModelImage of that data. - * Any changes to the GraphicsImage that occur only on the GPU can first be written from - * the GPU back into the GraphicsImage CPU data storage. This enables calculations that - * are performed on the GPU to be written back into a ModelImage data structure. - * - * @param kImage Graphics Image to copy - * @param bSwap when true convert from RGBA (graphics format) to ARGB (ModelImage format) - * @return new ModelImage from Volume Texture on GPU. - */ - public static ModelImage CreateImageFromTexture(final GraphicsImage kImage, final boolean bSwap) { - final int iXBound = kImage.GetBound(0); - final int iYBound = kImage.GetBound(1); - final int iZBound = kImage.GetBound(2); - final int iSize = iXBound * iYBound * iZBound; - final int[] extents = new int[] {iXBound, iYBound, iZBound}; - - ModelImage kResult = null; - if (kImage.GetFormat() == GraphicsImage.FormatMode.IT_RGBA8888) { - byte[] aucData = kImage.GetData(); - if (bSwap) { - byte bVal = 0; - aucData = new byte[4 * iXBound * iYBound * iZBound]; - for (int i = 0; i < iSize; i += 4) { - if (kImage.GetData()[i + 1] > bVal) { - bVal = kImage.GetData()[i + 1]; - } - aucData[i] = kImage.GetData()[i + 3]; - aucData[i + 1] = kImage.GetData()[i + 1]; - aucData[i + 2] = kImage.GetData()[i + 2]; - aucData[i + 3] = kImage.GetData()[i]; - //System.err.println( kImage.GetData()[i + 3] + " " + kImage.GetData()[i + 1] + " " + kImage.GetData()[i + 2] ); - } - // System.err.println( bVal ); - } - try { - kResult = new ModelImage(ModelStorageBase.ARGB, extents, ""); - kResult.importData(0, aucData, true); - } catch (final IOException e) { - e.printStackTrace(); - } - } else { - final byte[] aiImageData = kImage.GetData(); - try { - kResult = new ModelImage(ModelStorageBase.UBYTE, extents, ""); - kResult.importData(0, aiImageData, true); - } catch (final IOException e) { - e.printStackTrace(); - } - } - return kResult; - } - - /** - * Initialize the textures for the color lookup table. - * - * @param kLUT the new LUT. - * @param kRGBT the new RGB table. - * @param kPostfix the string postfix to concatenate to the "ColorMap" image name. - * @return GraphicsImage, the new GraphicsImage storing the colormap lookup table. - */ - public static GraphicsImage InitColorMap( Texture kTexture, GraphicsImage kImage, final ModelStorageBase kLUT, final String kPostFix) { - byte[] aucData; - if ( kImage == null ) - { - aucData = new byte[256 * 4]; - } - else - { - aucData = kImage.GetData(); - } - if (kLUT instanceof ModelLUT ) { - // ModelImage is Color, initialize the ModelRGB - ModelLUT.exportIndexedLUTMin((ModelLUT)kLUT, aucData); - } - else if (kLUT instanceof ModelRGB ) { - // Initialize the ModelLUT - ModelLUT.exportIndexedLUTMin((ModelRGB)kLUT, aucData); - } - if ( kImage == null ) - { - // Return the new GraphicsImage containing the table data: - return new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, 256, aucData, new String("ColorMap" + kPostFix)); - } - if ( kTexture != null ) - { - kTexture.Reload(true); - } - return kImage; - } - - - private GraphicsImage initColorMap() { - final byte[] aucData = new byte[256 * 4]; - if (m_kRGBT != null) - { - // ModelImage is Color, initialize the ModelRGB - ModelLUT.exportIndexedLUTMin(m_kRGBT, aucData); - } else if ( m_kLUT != null ) - { - // Initialize the ModelLUT - ModelLUT.exportIndexedLUTMin(m_kLUT, aucData); - } - // Return the new GraphicsImage containing the table data: - return new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, 256, 1, aucData, new String("ColorMap" + m_kImage.getImageName() + m_kPostfix)); - } - - - /** - * When a ModelImage changes on the CPU, this function is used to update the ModelImage - * data on the CPU. - * - * @param kImage Modified ModelImage to copy into the GPU Texture and GraphicsImage - * @param iTimeSlice time value for 4D image, 0 otherwise - * @param kNewImage a new ModelImage (always 3D) that the data or data subset for 4D image can be copied into (when non-null). - * @param kVolumeImage GraphicsImage that will hold the ModelImage data - * @param kVolumeTexture Texture object containing the GraphicsImage - * @param kImageName new image name for the new ModelImage. - * @param bSwap when true swap the ARGB (ModelImage) color data representation to a RGBA (GPU) color representation. - * @return - */ - public static GraphicsImage UpdateData(final ModelImage kImage, final int iTimeSlice, final ModelImage kNewImage, - final GraphicsImage kVolumeImage, final Texture kVolumeTexture, final String kImageName, - final boolean bSwap, final boolean bRescale) { - GraphicsImage kReturn = kVolumeImage; - final int iXBound = kImage.getExtents()[0]; - final int iYBound = kImage.getExtents()[1]; - final int iZBound = kImage.getExtents()[2]; - - byte[] aucData = null; - int iSize = iXBound * iYBound * iZBound; - if (kImage.isColorImage()) { - iSize *= 4; - aucData = new byte[iSize]; - try { - kImage.exportDataUseMask(iTimeSlice * iSize, iSize, bRescale, aucData); - if (bSwap) { - for (int i = 0; i < iSize; i += 4) { - final byte tmp = aucData[i]; - aucData[i] = aucData[i + 1]; - aucData[i + 1] = aucData[i + 2]; - aucData[i + 2] = aucData[i + 3]; - aucData[i + 3] = tmp; - } - } - } catch (final IOException e) { - e.printStackTrace(); - } - if (kReturn == null) { - kReturn = new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, aucData, - kImageName); - } else { - kReturn.SetData(aucData, iXBound, iYBound, iZBound); - } - } else { - aucData = new byte[iSize]; - try { - kImage.exportDataUseMask(iTimeSlice * iSize, iSize, bRescale, aucData); - // Temporary make the texture an RGBA texture until JOGL2 fixes NPOT textures. - byte[] aucData2 = new byte[iSize*4]; - for (int i = 0; i < iSize; i++) { - aucData2[i * 4 + 0] = aucData[i]; - aucData2[i * 4 + 1] = aucData[i]; - aucData2[i * 4 + 2] = aucData[i]; - aucData2[i * 4 + 3] = 1; - } - - if (kReturn == null) { - kReturn = new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, aucData2, - kImageName); - } else { - kReturn.SetData(aucData2, iXBound, iYBound, iZBound); - } - } catch (final IOException e) { - e.printStackTrace(); - } - - } - if (kNewImage != null) { - try { - kNewImage.importData(0, aucData, true); - } catch (final IOException e) {} - } - if (kVolumeTexture != null) { - kVolumeTexture.Reload(true); - } - return kReturn; - } - - private GraphicsImage initVolumeData(final ModelImage kImage, final int iTimeSlice, - final Texture kVolumeTexture, final String kImageName, final boolean bSwap, final boolean bRescale) - { - GraphicsImage kReturn = null; - final int iXBound = kImage.getExtents()[0]; - final int iYBound = kImage.getExtents()[1]; - final int iZBound = kImage.getExtents()[2]; - - byte[] aucData = null; - int iSize = iXBound * iYBound * iZBound; - if (kImage.isColorImage()) { - iSize *= 4; - aucData = new byte[iSize]; - try { - kImage.exportDataUseMask(iTimeSlice * iSize, iSize, bRescale, aucData); - if (bSwap) { - for (int i = 0; i < iSize; i += 4) { - final byte tmp = aucData[i]; - aucData[i] = aucData[i + 1]; - aucData[i + 1] = aucData[i + 2]; - aucData[i + 2] = aucData[i + 3]; - aucData[i + 3] = tmp; - } - } - } catch (final IOException e) { - e.printStackTrace(); - } - kReturn = new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, aucData, - kImageName); - } else { - aucData = new byte[iSize]; - try { - kImage.exportDataUseMask(iTimeSlice * iSize, iSize, bRescale, aucData); - // Temporary make the texture an RGBA texture until JOGL2 fixes NPOT textures. - byte[] aucData2 = new byte[iSize*4]; - for (int i = 0; i < iSize; i++) { - aucData2[i * 4 + 0] = aucData[i]; - aucData2[i * 4 + 1] = aucData[i]; - aucData2[i * 4 + 2] = aucData[i]; - aucData2[i * 4 + 3] = 1; - } - - kReturn = new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, aucData2, - kImageName); - } catch (final IOException e) { - e.printStackTrace(); - } - - } - if (kVolumeTexture != null) { - kVolumeTexture.Reload(true); - } - return kReturn; - } - - private GraphicsImage resetVolumeData(final ModelImage kImage, final int iTimeSlice, GraphicsImage kGraphicsImage, - final Texture kVolumeTexture, final String kImageName, final boolean bSwap, final boolean bRescale) - { - final int iXBound = kImage.getExtents()[0]; - final int iYBound = kImage.getExtents()[1]; - final int iZBound = kImage.getExtents()[2]; - int iSize = iXBound * iYBound * iZBound; - - byte[] aucData = null; - if (kImage.isColorImage()) { - iSize *= 4; - aucData = kGraphicsImage.GetData(); - try { - kImage.exportDataUseMask(iTimeSlice * iSize, iSize, bRescale, aucData); - if (bSwap) { - for (int i = 0; i < iSize; i += 4) { - final byte tmp = aucData[i]; - aucData[i] = aucData[i + 1]; - aucData[i + 1] = aucData[i + 2]; - aucData[i + 2] = aucData[i + 3]; - aucData[i + 3] = tmp; - } - } - } catch (final IOException e) { - e.printStackTrace(); - } - } - else { - aucData = new byte[iSize]; - try { - kImage.exportDataUseMask(iTimeSlice * iSize, iSize, bRescale, aucData); - byte[] aucData2 = kGraphicsImage.GetData(); - for (int i = 0; i < iSize; i++) { - aucData2[i * 4 + 0] = aucData[i]; - aucData2[i * 4 + 1] = aucData[i]; - aucData2[i * 4 + 2] = aucData[i]; - aucData2[i * 4 + 3] = 1; - } - } catch (final IOException e) { - e.printStackTrace(); - } - - } - if (kVolumeTexture != null) { - kVolumeTexture.Reload(true); - } - return kGraphicsImage; - } - - private void addNormals(final ModelImage kImage, final int iTimeSlice) { - final int iXBound = kImage.getExtents()[0]; - final int iYBound = kImage.getExtents()[1]; - final int iZBound = kImage.getExtents()[2]; - - int iSize = iXBound * iYBound * iZBound; - byte[] aucData = new byte[iSize * 4]; - try { - kImage.exportDataUseMask(0, iSize * 4, true, aucData); - byte[] volumeData = m_kVolume[iTimeSlice].GetData(); - for (int i = 0; i < iSize; i++) { - volumeData[i*4 + 1] = aucData[i*4 + 1]; - volumeData[i*4 + 2] = aucData[i*4 + 2]; - volumeData[i*4 + 3] = aucData[i*4 + 3]; - } - } catch (final IOException e) { - e.printStackTrace(); - } - m_kVolumeTarget.Reload(true); - } - - - private GraphicsImage createGM_Laplace(final ModelImage kImageGM, final ModelImage kImageL, - final GraphicsImage kVolumeImage, - final int iTimeSlice, final boolean bSwap) { - - GraphicsImage kReturn = kVolumeImage; - final int iXBound = kImageGM.getExtents()[0]; - final int iYBound = kImageGM.getExtents()[1]; - final int iZBound = kImageGM.getExtents()[2]; - - int iSize = iXBound * iYBound * iZBound; - byte[] aucDataL = new byte[iSize]; - - if ( kImageL != null ) - { - try { - kImageL.exportDataUseMask(0, iSize, false, aucDataL); - } catch (final IOException e) { - e.printStackTrace(); - } - } - - byte[] aucDataGM = null; - if (kImageGM.isColorImage()) { - iSize *= 4; - aucDataGM = new byte[iSize]; - try { - kImageGM.exportDataUseMask(0, iSize, false, aucDataGM); - if (bSwap) { - for (int i = 0, j = 0; i < iSize; i += 4) { - aucDataGM[i] = aucDataGM[i + 1]; - aucDataGM[i + 1] = aucDataGM[i + 2]; - aucDataGM[i + 2] = aucDataGM[i + 3]; - if ( kImageL != null ) - { - aucDataGM[i + 3] = aucDataL[j++]; - } - } - } - kImageGM.importData( 0, aucDataGM, false ); - } catch (final IOException e) { - e.printStackTrace(); - } - if (kReturn == null) { - kReturn = new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, aucDataGM, - kImageGM.getImageName()); - } else { - kReturn.SetData(aucDataGM, iXBound, iYBound, iZBound); - } - } - else - { - try { - aucDataGM = new byte[iSize]; - kImageGM.exportDataUseMask(0, iSize, false, aucDataGM); - byte[] aucData2 = new byte[iSize*4]; - for (int i = 0; i < iSize; i++) { - aucData2[i * 4 + 0] = aucDataGM[i]; - aucData2[i * 4 + 1] = aucDataGM[i]; - aucData2[i * 4 + 2] = aucDataGM[i]; - if ( kImageL != null ) - { - aucData2[i * 4 + 3] = aucDataL[i]; - } - } - - if (kReturn == null) { - kReturn = new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, aucData2, - kImageGM.getImageName()); - } else { - kReturn.SetData(aucData2, iXBound, iYBound, iZBound); - } - } catch (final IOException e) { - e.printStackTrace(); - } catch ( java.lang.OutOfMemoryError e ) { - return null; - } - aucDataGM = null; - } - return kReturn; - } - - - - /** - * Creates a new GraphicsImage for the input ModelSimpleImage. The ModelSimpleImage data is - * referenced by the new GraphicsImage and will be passed to the GPU as a texture. - * @param kImage input ModelSimpleImage. - * @param kImageName name for the GraphicsImage. - * @return a new GraphcisImage. - */ - public static GraphicsImage UpdateData(final ModelSimpleImage kImage, final String kImageName) { - final GraphicsImage.FormatMode eType = GraphicsImage.FormatMode.IT_L32F; - - if (kImage.nDims == 3) { - return new GraphicsImage(eType, kImage.extents[0], kImage.extents[1], kImage.extents[2], kImage.data, - kImageName); - } - return new GraphicsImage(eType, kImage.extents[0], kImage.extents[1], 1, kImage.data, kImageName); - } - - /** - * When the LUT changes, this function updates the LUT data on the GPU. - * - * @param kColorTexture the color-map Texture object. - * @param kColorMap the color-map GraphicsImage object (stores data). - * @param kLUT the updated or new LUT. - */ - public static void UpdateImages(final Texture kColorTexture, final GraphicsImage kColorMap, final ModelLUT kLUT) { - if (kLUT == null) { - return; - } - ModelLUT.exportIndexedLUTMin(kLUT, kColorMap.GetData()); - kColorTexture.Reload(true); - } - - /** - * When the ModelImage data is rendered as a solid surface, the Normal map is used in the rendering. - * The Normal map is calculated on the GPU by one of the GLSL shader programs. This function is called - * after the GPU calculation has finished and the GPU data has been copied into a new ModelImage on the CPU - * the new ModelImage then contains the Normal map information, which is written into a file and - * copied into the Normal map GraphicsImage used to render the original ModelImage. - * - * @param i current 3D sub-image for 4D data. If the data is 3D this value should be 0. - * @param kImage a new ModelImage containing the calculated Normals. - */ - public void CopyNormalFiles(final int i, final ModelImage kImage) { - final String kImageName = ModelImage.makeImageName(m_kImage.getFileInfo(0).getFileName(), "_Normal_" - + i); - JDialogBase.updateFileInfo( m_kImage, kImage ); - ModelImage.saveImage( kImage, kImageName + ".xml", m_kDir, false ); - if ( m_kImage.isColorImage() ) - { - m_kNormal[i] = initVolumeData(kImage, 0, m_kNormalMapTarget, kImage.getImageName(), true, true); - } - else - { - addNormals(kImage, i); - } - } - - /** - * Read the current Volume Texture from the GPU and return a new ModelImage of that data. - * - * @return new ModelImage from Volume Texture on GPU. - */ - public static ModelImage CreateImageFromTexture(final GraphicsImage kImage) { - final int[] extents = new int[ kImage.GetDimension() ]; - for ( int i = 0; i < extents.length; i++ ) - { - extents[i] = kImage.GetBound(i); - } - - //GraphicsImage.Type eType = kImage.GetType(); - int type = ModelStorageBase.ARGB; - /* - switch ( eType ) - { - case IT_BYTE : type = ModelStorageBase.BYTE; break; - case IT_UBYTE : type = ModelStorageBase.UBYTE; break; - case IT_SHORT : type = ModelStorageBase.SHORT; break; - case IT_USHORT : type = ModelStorageBase.USHORT; break; - case IT_INT : type = ModelStorageBase.INTEGER; break; - case IT_UINT : type = ModelStorageBase.UINTEGER; break; - case IT_LONG : type = ModelStorageBase.LONG; break; - case IT_FLOAT : type = ModelStorageBase.FLOAT; break; - case IT_DOUBLE : type = ModelStorageBase.DOUBLE; break; - } - */ - final ModelImage kResult = new ModelImage(type, extents, kImage.GetName() ); - int size = kImage.GetQuantity(); - for ( int i = 0; i < size;i++ ) - { - kResult.set(i*4 + 1, kImage.GetData()[i*3 + 0]); - kResult.set(i*4 + 2, kImage.GetData()[i*3 + 1]); - kResult.set(i*4 + 3, kImage.GetData()[i*3 + 2]); - } - return kResult; - } - - /** - * Memory cleanup. - */ - public void dispose() { - if (m_kImage == null) { - return; - } - m_kImage.disposeLocal(); - m_kImage = null; - - for (final GraphicsImage element : m_kVolume) { - element.dispose(); - } - m_kVolume = null; - m_kVolumeTarget.dispose(); - m_kVolumeTarget = null; - - if ( m_kNormal != null ) - { - for (final GraphicsImage element : m_kNormal) { - element.dispose(); - } - m_kNormal = null; - } - if ( m_kNormalMapTarget != null ) - { - m_kNormalMapTarget.dispose(); - m_kNormalMapTarget = null; - } - - m_kScratchTarget.dispose(); - m_kScratchTarget = null; - - m_kColorMap.dispose(); - m_kColorMap = null; - m_kColorMapTarget.dispose(); - m_kColorMapTarget = null; - - if ( m_kImageGM != null ) - { - m_kImageGM.disposeLocal(); - m_kImageGM = null; - } - - for (final GraphicsImage element : m_kVolumeGM) { - if ( element != null ) - element.dispose(); - } - m_kVolumeGM = null; - m_kVolumeGMTarget.dispose(); - m_kVolumeGMTarget = null; - - - m_kOpacityMap_GM.dispose(); - m_kOpacityMap_GM = null; - m_kOpacityMapTarget_GM.dispose(); - m_kOpacityMapTarget_GM = null; - - if (m_kSurfaceImage != null) { - m_kSurfaceImage.dispose(); - m_kSurfaceImage = null; - m_kSurfaceTarget.dispose(); - m_kSurfaceTarget = null; - } - - m_kLUT = null; - m_kPostfix = null; - if ( m_kHisto != null ) - { - for (final GraphicsImage element : m_kHisto) { - if ( element != null ) - { - element.dispose(); - } - } - m_kHisto = null; - } - m_akHistoTCoord = null; - } - - - /** - * This function is called when the user selects the Surface or Composite Surface volume rendering option. - * If the normals have already been initialized the function returns. Otherwise the function checks if the - * normals are available in a file on disk, and if so if they match the parameters (size, units, resolutions) of - * the original ModelImage. If the files match they are used and the Normal map is read from file. Otherwise this - * function launches the GPU-based Normal calculation. That calculation when finished calls the CopyNormalFiles - * which writes the calculated normals to disk and updates the Normal map on the GPU for rendering. - */ - public void GenerateNormalFiles( VolumeTriPlanarInterface parentFrame ) { - if ( m_bNormalsInit ) - { - return; - } - if ( !m_bNormalsInit ) - { - int dimX = m_kImage.getExtents().length > 0 ? m_kImage.getExtents()[0] : 1; - int dimY = m_kImage.getExtents().length > 1 ? m_kImage.getExtents()[1] : 1; - int dimZ = m_kImage.getExtents().length > 2 ? m_kImage.getExtents()[2] : 1; - ModelImage outputImage = new ModelImage( ModelStorageBase.ARGB_FLOAT, new int[]{dimX,dimY,dimZ}, "temp" ); - for (int i = 0; i < m_iTimeSteps; i++) { - OpenCLAlgorithmVolumeNormals oclNormals = new OpenCLAlgorithmVolumeNormals( m_kImage, outputImage, CL.CL_DEVICE_TYPE_GPU ); - oclNormals.setTime(i); - oclNormals.run(); - if ( m_kImage.isColorImage() ) - { - m_kNormal[i] = initVolumeData(outputImage, 0, m_kNormalMapTarget, outputImage.getImageName(), true, true); - } - else - { - addNormals(outputImage, i); - } - } - m_bNormalsInit = true; - - if ( m_kNormal != null) - { - m_kNormalMapTarget.SetImage(m_kNormal[m_iTimeSlice]); - m_kNormalMapTarget.Reload(true); - } - outputImage.disposeLocal(); - } - } - - /** - * Return the Color Map Texture. - * @return Volume color map Texture. - */ - public Texture GetColorMapTarget() { - return m_kColorMapTarget; - } - - /** - * Return the normalization factor for DDR rendering mode. - * @return normalization factor for DDR rendering mode. - */ - public float getDRRNorm() { - return m_fDRRNormalize; - } - - /** - * Return the Gradient Magnitude Texture. - * @return Gradient Magnitude Texture. - */ - public Texture GetGradientMapTarget() { - return m_kVolumeGMTarget; - } - - /** - * Returns true if the multi-histogram histogram texture has been initialized, false otherwise. - * @return true if the multi-histogram histogram texture has been initialized, false otherwise. - */ - public boolean isHistoInit() - { - return m_bHistoInit; - } - - /** - * Returns the multi-histogram histogram Texture. - * @return the multi-histogram histogram Texture. - */ - public Texture GetHistoTarget() { - return m_kHistoTarget; - } - - -// private ModelImage[] m_akHistogram; -// public ModelImage GetHistogram() { -// -// if ( !m_bHistoInit ) -// { -// SetGradientMagnitude(null, true, m_kPostfix); -// } -// if ( m_akHistogram == null ) -// { -// m_akHistogram = new ModelImage[m_akImages.length]; -// } -// if ( m_akHistogram[m_iTimeSlice] == null ) -// { -// m_akHistogram[m_iTimeSlice] = new ModelImage(ModelStorageBase.INTEGER, new int[]{256,256}, "JointHisto" + m_iTimeSlice); -// try { -// m_akHistogram[m_iTimeSlice].importData(m_kHisto[m_iTimeSlice].GetData()); -// } catch (IOException e) { -// e.printStackTrace(); -// } -// } -// return m_akHistogram[m_iTimeSlice]; -// } - - /** - * Return the texture coordinates for the multi-histogram histogram texture. - * @return the texture coordinates for the multi-histogram histogram texture. - */ - public Vector2f[] GetHistoTCoords() { - return m_akHistoTCoord; - } - - public ModelImage GetGradientMagnitudeImage() - { - return m_kImageGM; - } - - public Vector2f GetGradientMagnitudeMinMax() - { - return m_akGradientMagMinMax[m_iTimeSlice]; - } - - public float GetGradientMagnitudeMin() - { - return m_akGradientMagMinMax[m_iTimeSlice].X; - } - - public float GetGradientMagnitudeMax() - { - return m_akGradientMagMinMax[m_iTimeSlice].Y; - } - - /** - * Return the ModelImage volume data. - * @return ModelImage volume data. - */ - public ModelImage GetImage() { - return m_kImage; - } - - /** - * Return the ModelImage LUT. - * @return Volume LUT. - */ - public ModelLUT GetLUT() { - return m_kLUT; - } - - /** - * Return the Normal map Texture. - * @return Normal map Texture. - */ - public Texture GetNormalMapTarget() { - return m_kNormalMapTarget; - } - - public Texture GetScratchTarget() { - return m_kScratchTarget; - } - - /** - * Return the gradient magnitude opacity transfer function Texture. - * @return gradient magnitude opacity transfer function Texture. - */ - public Texture GetOpacityMapGMTarget() { - return m_kOpacityMapTarget_GM; - } - - /** - * Return the postfix for this VolumeImage. - * @return postfix for this VolumeImage. - */ - public String GetPostfix() { - return m_kPostfix; - } - - /** - * Return the Volume RGBT. - * @return Volume RGBT. - */ - public ModelStorageBase getLUT() { - return (m_kImage != null ) ? m_kImage.isColorImage() ? m_kRGBT : m_kLUT : null; - } - - /** - * Return the Volume RGBT. - * @return Volume RGBT. - */ - public ModelRGB GetRGB() { - return m_kRGBT; - } - - - public float GetTransferedValue( int x, int y, int z ) - { - int dimX = m_kImage.getExtents().length > 0 ? m_kImage.getExtents()[0] : 1; - int dimY = m_kImage.getExtents().length > 1 ? m_kImage.getExtents()[1] : 1; - int dimZ = m_kImage.getExtents().length > 2 ? m_kImage.getExtents()[2] : 1; - if ( x < 0 || x >= dimX || y < 0 || y >= dimY || z < 0 || z >= dimZ ) return -1; - - if ( m_kImage.isColorImage() ) { - float r = m_kRGBT.getROn() ? TransferValue(m_kImage.getFloat(x, y, z, 1)) : -1; - float g = m_kRGBT.getGOn() ? TransferValue(m_kImage.getFloat(x, y, z, 2)) : -1; - float b = m_kRGBT.getBOn() ? TransferValue(m_kImage.getFloat(x, y, z, 3)) : -1; - return Math.max( r, Math.max(g, b)); - } - float value = m_kImage.getFloat(x, y, z); - return TransferValue(value); - } - - - public ColorRGBA GetTransferedValueOld( float x, float y, float z ) - { - float value = m_kImage.getFloatTriLinearBounds(x, y, z); - - float min = (float) m_kImage.getMin(); - float max = (float) m_kImage.getMax(); - float diff = max - min; - byte index = 0; - if ( (diff > 1) && (diff <= 255) ) - { - index = (byte)(((value - min)/diff) * diff); - } - else - { - index = (byte)(((value - min)/diff) * 255); - } - if ( (index >= 0) && (index < 255) && (m_kColorMap != null) && (m_kColorMap.GetData() != null) ) - { - byte r = m_kColorMap.GetData()[index * 4 + 0]; - byte g = m_kColorMap.GetData()[index * 4 + 1]; - byte b = m_kColorMap.GetData()[index * 4 + 2]; - byte a = m_kColorMap.GetData()[index * 4 + 3]; - return new ColorRGBA(r, g, b, a); - } - return new ColorRGBA(0,0,0,0); - } - - - private float TransferValue(float value) { - float min = (float) m_kImage.getMin(); - float max = (float) m_kImage.getMax(); - float diff = max - min; - byte index = 0; - if ( (diff > 1) && (diff <= 255) ) - { - index = (byte)(((value - min)/diff) * diff); - } - else - { - index = (byte)(((value - min)/diff) * 255); - } - if ( (index >= 0) && (index < 255) && (m_kColorMap != null) && (m_kColorMap.GetData() != null) ) - { - byte r = m_kColorMap.GetData()[index * 4 + 0]; - byte g = m_kColorMap.GetData()[index * 4 + 1]; - byte b = m_kColorMap.GetData()[index * 4 + 2]; - byte a = m_kColorMap.GetData()[index * 4 + 3]; - return Math.max(r*a, Math.max(g*a, b*a)); - } - return -1; - } - - - - /** - * The ModelImage Volume max-scale factor. - * @return Volume max-scale factor. - */ - public float GetScaleMax() { - return m_fMax; - } - - public int GetMaxExtent() - { - return m_iMaxExtent; - } - - /** - * The ModelImage Volume x-scale factor. - * @return Volume x-scale factor. - */ - public float GetScaleX() { - return m_fX; - } - - /** - * The ModelImage Volume y-scale factor. - * @return Volume y-scale factor. - */ - public float GetScaleY() { - return m_fY; - } - - /** - * The ModelImage Volume z-scale factor. - * @return Volume z-scale factor. - */ - public float GetScaleZ() { - return m_fZ; - } - - /** - * Return the surface mask Texture. - * @return surface mask Texture. - */ - public Texture GetSurfaceTarget() { - return m_kSurfaceTarget; - } - - - - /** A vector of BitSet masks, one for each surface loaded into the viewer. */ - protected Vector surfaceMask; - /** A vector of the mask names, so they can be accessed by name: */ - protected Vector surfaceNames; - /** A vector of BitSet masks, one for each surface loaded into the viewer. */ - protected Vector surfaceColor; - /** - * Add a new surface mask. - * @param name surface name. - * @param mask surface mask volume. - */ - public void setSurfaceMask(String name, ColorRGB color, BitSet mask) - { - if ( surfaceMask == null ) - { - surfaceMask = new Vector(); - surfaceNames = new Vector(); - surfaceColor = new Vector(); - } - surfaceMask.add(mask); - surfaceNames.add(name); - surfaceColor.add(color); - updateMask(); - } - - private void updateMask() - { - boolean bUpdate = false; - final int iXBound = m_kImage.getExtents()[0]; - final int iYBound = m_kImage.getExtents()[1]; - final int iZBound = m_kImage.getExtents()[2]; - int length = iXBound * iYBound * iZBound; - for ( int i = 0; i < length; i++ ) - { - boolean color = false; - for ( int surface = 0; surface < surfaceMask.size(); surface++ ) - { - if ( surfaceMask.elementAt(surface).get(i) ) - { - m_kSurfaceImage.GetData()[i * 4 + 0] = (byte) (surfaceColor.elementAt(surface).R * 255); - m_kSurfaceImage.GetData()[i * 4 + 1] = (byte) (surfaceColor.elementAt(surface).G * 255); - m_kSurfaceImage.GetData()[i * 4 + 2] = (byte) (surfaceColor.elementAt(surface).B * 255); - m_kSurfaceImage.GetData()[i * 4 + 3] = (byte) (255); - bUpdate = true; - color = true; - } - } - if ( !color ) - { - m_kSurfaceImage.GetData()[i * 4 + 0] = (byte) (0); - m_kSurfaceImage.GetData()[i * 4 + 1] = (byte) (0); - m_kSurfaceImage.GetData()[i * 4 + 2] = (byte) (0); - m_kSurfaceImage.GetData()[i * 4 + 3] = (byte) (0); - } - } - if ( bUpdate ) - { - m_kSurfaceTarget.Reload(true); - } - } - - /** - * Delete the surface mask, using the name of the mask as reference. - * @param name the surface name. - */ - public void removeSurfaceMask(String name) - { - boolean bUpdate = false; - if ( surfaceMask != null && surfaceNames != null) - { - if ( surfaceNames.contains(name) ) - { - surfaceMask.remove( surfaceNames.indexOf(name) ); - surfaceColor.remove( surfaceNames.indexOf(name) ); - surfaceNames.remove(name); - bUpdate = true; - } - } - if ( bUpdate ) - { - updateMask(); - m_kSurfaceTarget.Reload(true); - } - } - - /** - * Delete the surface mask, using the name of the mask as reference. - * @param name the surface name. - */ - public void setSurfaceMaskColor(String name, ColorRGB color) - { - boolean bUpdate = false; - if ( surfaceMask != null && surfaceNames != null) - { - if ( surfaceNames.contains(name) ) - { - surfaceColor.elementAt( surfaceNames.indexOf(name) ).Copy(color); - bUpdate = true; - } - } - if ( bUpdate ) - { - updateMask(); - } - } - - - /** - * Returns the current rendered time-slice for 4D images. Otherwise returns 0. - * @return the current rendered time-slice for 4D images. Otherwise returns 0. - */ - public int GetTimeSlice() { - return m_iTimeSlice; - } - - /** - * Return the Texture containing the volume data. - * @return Texture containing the volume data. - */ - public Texture GetVolumeTarget() { - return m_kVolumeTarget; - } - - /** - * Return the Buffer containing the volume data, which is stored in the Texture GrapicsImage. - * @return Buffer containing the volume data. - */ - public Buffer GetVolumeTargetBuffer() { - return m_kVolumeTarget.GetImage().GetDataBuffer(); - } - - /** - * Initialize the GraphicsImage for the opacity lookup table. - * - * @param kImage the ModelImage the opacity transfer function applies to. - * @param kPostfix the string postfix to concatenate to the "OpacityMap" image name. - * @return GraphicsImage, the new GraphicsImage storing opacity lookup table. - */ - public GraphicsImage InitOpacityMap(final ModelImage kImage, final String kPostFix) { - final int iLutHeight = 256; - final float[] afData = new float[iLutHeight]; - final float fRange = (float) (kImage.getMax() - kImage.getMin()); - final float fStep = fRange / iLutHeight; - float fDataValue = (float) kImage.getMin(); - for (int i = 0; i < iLutHeight; i++) { - afData[i] = (float) (iLutHeight * (kImage.getMax() - fDataValue) / fRange); - fDataValue += fStep; - } - - return new GraphicsImage(GraphicsImage.FormatMode.IT_L8, iLutHeight, afData, - new String("OpacityMap" + kPostFix)); - } - - /** - * Return true if the Volume image is a color image. - * - * @return true if the Volume image is a color image. - */ - public boolean IsColorImage() { - return m_kImage.isColorImage(); - } - - /** - * Release the Textures containing the volume data. Once Textures are released, they will be re-loaded onto the GPU - * during the next frame. - */ - public void ReleaseVolume() { - m_kVolumeTarget.Reload(true); - } - - - - /** - * Called when the user selects the Gradient Magnitude option or the Multi-Histogram option - * in the Volume Renderer. - * @param kGradientMagnitude pre-computed GradientMagnitude image or null - * @param bComputeLaplace when true the Laplace image and multi-histogram histogram Textures are computed. - * @param kPostfix GraphicsImage postfix string. - */ - public void SetGradientMagnitude(ModelImage kGradientMagnitude, boolean bComputeLaplace, String kPostfix ) - { - int start = 0; - int end = m_kImage.getNDims() > 3 ? m_iTimeSteps : 1; - if ( !m_bGMInit ) - { - try { - // System.err.println( "SetGradientMagnitude " + 0 ); - m_kImageGM = getGradientMagnitude( m_kImage, 0 ); - if ( m_kImageGM != null ) - { - m_kVolumeGM[0] = createGM_Laplace(m_kImageGM, null, m_kVolumeGM[0], 0, true); - if ( m_kVolumeGM[0] != null ) - { - m_akGradientMagMinMax[0] = new Vector2f( (float)m_kImageGM.getMin(), (float)m_kImageGM.getMax() ); - m_kVolumeGMTarget.SetImage(m_kVolumeGM[0]); - m_kVolumeGMTarget.Reload(true); - m_bGMInit = true; - } - } - for (int i = 1; i < m_iTimeSteps; i++) - { - // System.err.println( "SetGradientMagnitude " + i ); - ModelImage gmImage = getGradientMagnitude( m_kImage, i ); - if ( gmImage != null ) - { - m_kVolumeGM[i] = createGM_Laplace(gmImage, null, m_kVolumeGM[i], i, true); - if ( m_kVolumeGM[i] != null ) - { - m_akGradientMagMinMax[i] = new Vector2f( (float)gmImage.getMin(), (float)gmImage.getMax() ); - gmImage.disposeLocal(); - end = i + 1; - } - } - } - } catch ( java.lang.OutOfMemoryError e ) {} - } - - if ( m_bGMInit && bComputeLaplace && (m_kImageGM != null)) - { - GenerateHistogram(m_kVolume, m_kVolumeGM, kPostfix, start, end ); - } - } - - /** - * Sets the ModelRGB for the iImage. - * - * @param kRGBT new ModelRGB - */ - public void SetRGBT(final ModelRGB kRGBT) { - if (kRGBT == null) { - return; - } - ModelLUT.exportIndexedLUTMin(kRGBT, m_kColorMap.GetData()); - m_kColorMapTarget.Reload(true); - m_kRGBT = kRGBT; - } - - /** - * Sets the time slices for 4D data. - * @param iSlice new time slice value. - */ - public void SetTimeSlice(final int iSlice) { - if (m_iTimeSlice != iSlice) { - m_iTimeSlice = iSlice; - update4D(); - } - } - - /** - * Updates the current time slice. - * @param bForward when true the time advances on step forward or wraps to the beginning. - * When false the time moves backward. - */ - public void update4D(final boolean bForward) { - if (m_iTimeSteps == 1) { - return; - } - if (bForward) { - m_iTimeSlice++; - } else { - m_iTimeSlice--; - } - if (m_iTimeSlice >= m_iTimeSteps) { - m_iTimeSlice = 0; - } - if (m_iTimeSlice < 0) { - m_iTimeSlice = m_iTimeSteps - 1; - } - - update4D(); - } - - /** - * Update the image data. - * - * @param kImage the modified ModelImage - * @param bCopytoCPU when true the data is copied from the GPU GraphicsImage into the ModelImage - */ - public void UpdateData(final ModelImage kImage, boolean reload) { - m_kImage = kImage; - if ( m_kVolume == null ) { - m_kPostfix = ""; - init(null, 0, false); - return; - } - initLUT(); - if ( reload ) - { - if ( m_kVolume[m_iTimeSlice] != null ) - { - m_kVolume[m_iTimeSlice].dispose(); - } - m_kVolumeTarget.Remove(); - m_kVolume[m_iTimeSlice] = initVolumeData(m_kImage, m_iTimeSlice, m_kVolumeTarget, m_kImage.getImageName(), true, false); - m_kVolumeTarget.SetImage(m_kVolume[m_iTimeSlice]); - } - else - { - m_kVolume[m_iTimeSlice] = resetVolumeData(m_kImage, m_iTimeSlice, m_kVolume[m_iTimeSlice], m_kVolumeTarget, m_kImage - .getImageName(), true, false); - } - InitScale(); - } - - - /** - * Changes the underlying image data and LUT. If the new image data is a different size than - * then previous one, recreate the volume image on the GPU, otherwise just overwrite it with - * the new data. - * @param kImage - * @param kLUT - * @param reload - */ - public void UpdateData(final ModelImage kImage, ModelLUT kLUT, boolean reload) { - if ( kLUT == null && !kImage.isColorImage()) - { - UpdateData(kImage, reload); - return; - } - m_kImage = kImage; - m_kLUT = kLUT; - if ( reload ) - { - if ( m_kVolume[m_iTimeSlice] != null ) - { - m_kVolume[m_iTimeSlice].dispose(); - } - m_kVolumeTarget.Remove(); - m_kVolume[m_iTimeSlice] = initVolumeData(m_kImage, m_iTimeSlice, m_kVolumeTarget, m_kImage.getImageName(), true, false); - m_kVolumeTarget.SetImage(m_kVolume[m_iTimeSlice]); - } - else - { - m_kVolume[m_iTimeSlice] = resetVolumeData(m_kImage, m_iTimeSlice, m_kVolume[m_iTimeSlice], m_kVolumeTarget, m_kImage - .getImageName(), true, false); - } - InitScale(); - } - - /** - * Update the LUT for the ModelImage. - * - * @param kLUT new LUT for ModelImage. - */ - public void UpdateImages(final ModelLUT kLUT) { - if (kLUT != null) { - VolumeImage.UpdateImages(m_kColorMapTarget, m_kColorMap, kLUT); - m_kLUT = kLUT; - } - } - - /** - * Update the LUT for the ModelImage. - * - * @param kLUT new LUT for ModelImage. - */ - public void UpdateImages(final ModelStorageBase kLUT) { - if ( (kLUT != null) && (kLUT instanceof ModelLUT)) { - VolumeImage.UpdateImages(m_kColorMapTarget, m_kColorMap, (ModelLUT)kLUT); - m_kLUT = (ModelLUT)kLUT; -// System.err.println("UpdateImages " + m_kColorMapTarget.GetName() + " " + kLUT. -// System.err.println(" " + m_kColorMapTarget.GetID() ); - - } - if ( (kLUT != null) && (kLUT instanceof ModelRGB)) { - ModelLUT.exportIndexedLUTMin((ModelRGB)kLUT, m_kColorMap.GetData()); - m_kColorMapTarget.Reload(true); - m_kRGBT = (ModelRGB)kLUT; - } - } - - /** - * Update the transfer function for the image iImage. - * - * @param kTransfer the new opacity transfer function - * @param iImage the image to modify (0 = volume image, 2 = gradient mag) - * @param kImage GradientMagitude image. - * @return boolean true when updated, false otherwise. - */ - public boolean UpdateImages(final TransferFunction kTransfer, final int iImage, final ModelImage kImage) { - if (iImage == 0) { - return UpdateImages2(m_kImage, m_kColorMapTarget, m_kColorMap, kTransfer); - } else if ( (iImage == 2) && (kImage != null) && (m_kOpacityMapTarget_GM != null) && (m_kOpacityMap_GM != null)) { - return UpdateImages(kImage, m_kOpacityMapTarget_GM, m_kOpacityMap_GM, kTransfer); - } - return false; - } - - /** - * In order to map line integrals of image intensity to RGB colors where each color channel is 8 bits, it is - * necessary to make sure that the integrals are in [0,255]. Producing a theoretical maximum value of a line - * integral is not tractable in an application. This method constructs an approximate maximum by integrating along - * each line of voxels in the image with line directions parallel to the coordinate axes. The 'processRay' call - * adjusts the line integrals using the estimate, but still clamps the integrals to 255 since the estimate might not - * be the true maximum. - * - * @return float Integral normalization factor. - */ - protected float computeIntegralNormalizationFactor() { - final int iXBound = m_kImage.getExtents()[0]; - final int iYBound = m_kImage.getExtents()[1]; - final int iZBound = m_kImage.getExtents()[2]; - - byte[] aucData = null; - int iSize = iXBound * iYBound * iZBound; - if (m_kImage.isColorImage()) { - iSize *= 4; - } - - aucData = new byte[iSize]; - - try { - m_kImage.exportDataUseMask(0, iSize, false, aucData); - } catch (final IOException e) { - e.printStackTrace(); - } - - // compute image normalization factor - int iX, iY, iZ, iBase, iSteps; - float fMaxIntegral = 0.0f; - float fTStep, fIntegral; - - // fix y and z, integrate over x - for (iY = 0; iY < iYBound; iY++) { - - for (iZ = 0; iZ < iZBound; iZ++) { - iBase = iXBound * (iY + (iYBound * iZ)); - iSteps = iXBound - 1; - fIntegral = 0.5f * ( (aucData[iBase] & 0x0ff) + (aucData[iBase + iSteps] & 0x0ff)); - fTStep = 1.0f / iSteps; - - for (iX = 1; iX < iSteps; iX++) { - fIntegral += (aucData[iBase + iX] & 0x0ff); - } - - fIntegral *= fTStep; - - if (fIntegral > fMaxIntegral) { - fMaxIntegral = fIntegral; - } - } - } - final int iXYProduct = iXBound * iYBound; - // fix x and z, integrate over y - for (iX = 0; iX < iXBound; iX++) { - - for (iZ = 0; iZ < iZBound; iZ++) { - iBase = iX + (iXYProduct * iZ); - iSteps = iYBound - 1; - fIntegral = 0.5f * ( (aucData[iBase] & 0x0ff) + (aucData[iBase + (iXBound * iSteps)] & 0x0ff)); - fTStep = 1.0f / iSteps; - - for (iY = 1; iY < iSteps; iY++) { - fIntegral += (aucData[iBase + (iXBound * iY)] & 0x0ff); - } - - fIntegral *= fTStep; - - if (fIntegral > fMaxIntegral) { - fMaxIntegral = fIntegral; - } - } - } - - // fix x and y, integrate over z - for (iX = 0; iX < iXBound; iX++) { - - for (iY = 0; iY < iYBound; iY++) { - iBase = iX + (iXBound * iY); - iSteps = iZBound - 1; - fIntegral = 0.5f * ( (aucData[iBase] & 0x0ff) + (aucData[iBase + (iXYProduct * iSteps)] & 0x0ff)); - fTStep = 1.0f / iSteps; - - for (iZ = 1; iZ < iSteps; iZ++) { - fIntegral += (aucData[iBase + (iXYProduct * iZ)] & 0x0ff); - } - - fIntegral *= fTStep; - - if (fIntegral > fMaxIntegral) { - fMaxIntegral = fIntegral; - } - } - } - aucData = null; - return (fMaxIntegral > 0.0f) ? (1.0f / fMaxIntegral) : 0.00f; - } - - /** - * Checks that the two input images match extents, units of measure and resolutions. The images - * may had different sizes (3D or 4D) the first 3-dimensions must match. - * @param kImage1 - * @param kImage2 - * @return true if the images match extends, units and resolutions. - */ - public static boolean checkImage(ModelImage kImage1, ModelImage kImage2 ) - { - for ( int i = 0; i < Math.min( kImage1.getExtents().length, kImage2.getExtents().length ); i++ ) - { - if ( kImage1.getExtents()[i] != kImage2.getExtents()[i] ) - { - return false; - } - if ( kImage1.getUnitsOfMeasure()[i] != kImage2.getUnitsOfMeasure()[i] ) - { - return false; - } - if ( kImage1.getResolutions(0)[i] != kImage2.getResolutions(0)[i] ) - { - return false; - } - } - return true; - } - - /** - * Generate 2D histogram from the input image and the gradient-magnitude - * - * @param kImage input GraphicsImage containing the ModelImage data - * @param kImageGM input GraphcisImage containing the Gradient Magnitude data. - * @param kPostFix post-fix for the image name. - */ - private void GenerateHistogram(final GraphicsImage[] kImage, final GraphicsImage[] kImageGM, final String kPostFix, - int start, int end ) - { - int iTMinX = 255, iTMaxX = 0; - int iTMinY = 255, iTMaxY = 0; - float max = Float.MIN_VALUE; - float min = Float.MAX_VALUE; - m_kHisto = new GraphicsImage[m_iTimeSteps]; - for (int t = start; t < end; t++) { - float[] afCount = new float[256 * 256]; - for (int i = 0; i < 256 * 256; i++) { - afCount[i] = 0; - } - - int a1; - int a2; - final byte[] abHistoData = kImageGM[t].GetData(); - final byte[] abData = kImage[t].GetData(); - if (m_kImage.isColorImage()) { - int iHisto = 0; - for (int i = 0; i < abData.length; i += 4) { - int iR = (abData[i]); - int iG = (abData[i + 1]); - int iB = (abData[i + 2]); - //a1 = (iR * 0.299 + iG * 0.587 + iB * 0.114); - a1 = (iR + iG + iB)/3; - a1 = (a1 & 0x00ff); - - iR = (abHistoData[i]); - iG = (abHistoData[i + 1]); - iB = (abHistoData[i + 2]); - //a2 = (short) (iR * 0.299 + iG * 0.587 + iB * 0.114); - a2 = (iR + iG + iB)/3; - a2 = (a2 & 0x00ff); - afCount[a1 + a2 * 256] += 1; - iHisto++; - } - } - else { - int iHisto = 0; - for (int i = 0; i < abData.length; i += 4) { - a1 = abData[i]; - a1 = (a1 & 0x00ff); - a2 = (abHistoData[iHisto]); - a2 = (a2 & 0x00ff); - afCount[a1 + a2 * 256] += 1; - iHisto += 4; - } - } - max = Float.MIN_VALUE; - min = Float.MAX_VALUE; - for (int i = 0; i < 256 * 256; ++i) { - afCount[i] = (float) Math.log(afCount[i]+1); - max = Math.max(afCount[i], max); - min = Math.min(afCount[i], min); - } - //System.err.println( min + " " + max ); - final byte[] abHisto = new byte[256 * 256]; - //int maxB = Integer.MIN_VALUE; - //int minB = Integer.MAX_VALUE; - for (int i = 0; i < 256 * 256; ++i) { - abHisto[i] = new Float((afCount[i] / max) * 255f).byteValue(); - //maxB = ( iVal > maxB ) ? iVal : maxB; - //minB = ( iVal < minB ) ? iVal : minB; - } - afCount = null; - - int iMinX = 255, iMaxX = 0; - int iIndex = 0; - for (int i = 0; i < 256; i++) { - for (int j = 0; j < 256; j++) { - iIndex = i * 256 + j; - if (abHisto[iIndex] > 50) { - if (iMinX > j) { - iMinX = j; - } - if (j > iMaxX) { - iMaxX = j; - } - } - } - } - - int iMinY = 255, iMaxY = 0; - for (int j = 0; j < 256; j++) { - for (int i = 0; i < 256; i++) { - iIndex = i * 256 + j; - if (abHisto[iIndex] > 50) { - if (iMinY > i) { - iMinY = i; - } - if (i > iMaxY) { - iMaxY = i; - } - } - } - } - if (iTMinX > iMinX) { - iTMinX = iMinX; - } - if (iTMaxX < iMaxX) { - iTMaxX = iMaxX; - } - - if (iTMinY > iMinY) { - iTMinY = iMinY; - } - if (iTMaxY < iMaxY) { - iTMaxY = iMaxY; - } - - // iMinX = 0; iMaxX = 255; - // iMinY = 0; iMaxY = 255; - - m_kHisto[t] = new GraphicsImage(GraphicsImage.FormatMode.IT_L8, 256, 256, (byte[]) null, new String( - "VolumeImageHisto" + kPostFix)); - m_kHisto[t].SetData(abHisto, 256, 256); - /* - ModelImage kTestHisto2D = new ModelImage( ModelStorageBase.UBYTE, new int[]{256,256}, "Histo2D" ); - try { - kTestHisto2D.importData(abHisto); - } catch (IOException e) { - e.printStackTrace(); - } - kTestHisto2D.calcMinMax(); - new ViewJFrameImage( kTestHisto2D ); */ - } - - m_kHistoTarget = new Texture(); - m_kHistoTarget.SetImage(m_kHisto[0]); - m_kHistoTarget.SetShared(true); - m_kHistoTarget.SetFilterType(Texture.FilterType.LINEAR); - m_kHistoTarget.SetWrapType(0, Texture.WrapType.CLAMP_BORDER); - m_kHistoTarget.SetWrapType(1, Texture.WrapType.CLAMP_BORDER); - m_kHistoTarget.SetWrapType(2, Texture.WrapType.CLAMP_BORDER); - - iTMinX = 0; - iTMaxX = Math.max( iTMaxX, iTMaxY ); - m_akHistoTCoord = new Vector2f[4]; - m_akHistoTCoord[0] = new Vector2f(iTMinX / 255.0f, iTMinX / 255.0f); - m_akHistoTCoord[1] = new Vector2f(iTMaxX / 255.0f, iTMinX / 255.0f); - m_akHistoTCoord[2] = new Vector2f(iTMaxX / 255.0f, iTMaxX / 255.0f); - m_akHistoTCoord[3] = new Vector2f(iTMinX / 255.0f, iTMaxX / 255.0f); - //m_akHistoTCoord[0] = new Vector2f(0f, 0f); - //m_akHistoTCoord[1] = new Vector2f(1f, 0f); - //m_akHistoTCoord[2] = new Vector2f(1f, 1f); - //m_akHistoTCoord[3] = new Vector2f(0f, 1f); - m_bHistoInit = true; - } - - /** - * Calculates and stores the gradient magnitude images (3D or 4D) for the input image. Or reads from disk. - * The data is stored in the GraphicsImage data structures and will be passed to the GPU to use in rendering. - * - * @param kImage input image - * @param kGradientMagnitude input Gradient Magnitude image, or null. - * @param bComputeLaplace when true the Laplace image is also calculated or read from dis. - private void GradientMagnitudeImage(final ModelImage kImage, ModelImage kGradientMagnitude, - boolean bComputeLaplace) { - - - if ( !m_bGMInit ) - { - ModelImage[] kImageGM = new ModelImage[m_iTimeSteps]; - String[] kImageName = new String[m_iTimeSteps]; - for (int i = 0; i < m_iTimeSteps; i++) { - kImageName[i] = ModelImage.makeImageName(kImage.getFileInfo(0).getFileName(), new String("_GM_" + i)); - - if ( kGradientMagnitude != null && checkImage(kImage, kGradientMagnitude )) - { - m_kVolumeGM[i] = VolumeImage.UpdateData(kGradientMagnitude, i, null, m_kVolumeGM[i], - m_kVolumeGMTarget, m_kVolumeGM[i].GetName(), true, false); - ModelImage.saveImage( kGradientMagnitude, kImageName[i] + ".xml", m_kDir ); - } - else - { - kImageGM[i] = ReadFromDisk(kImageName[i] + ".xml", m_kDir); - if ( kImageGM[i] != null && !checkImage(kImage, kImageGM[i] ) ) - { - kImageGM[i].disposeLocal(); - kImageGM[i] = null; - } - if (kImageGM[i] == null) { - JDialogGradientMagnitude kCalcMagnitude = new JDialogGradientMagnitude(null, m_akImages[i]); - kCalcMagnitude.setVisible(false); - kCalcMagnitude.setOutputNewImage(true); - kCalcMagnitude.setDisplayProgressBar(true); - kCalcMagnitude.setSeparateThread(false); - kCalcMagnitude.setSeparable(true); - kCalcMagnitude.setUseOCL(true); - kCalcMagnitude.actionPerformed(new ActionEvent(this, 0, "OK")); - kImageGM[i] = kCalcMagnitude.getResultImage(); - kCalcMagnitude = null; - } - if (kImageGM[i] == null) { - System.err.println("Gradient magnitude calculation returned null"); - m_kVolumeGM[i] = VolumeImage.UpdateData(kImage, i, null, m_kVolumeGM[i], m_kVolumeGMTarget, kImageName[i], true, false); - } else { - kImageGM[i].calcMinMax(); - m_akGradientMagMinMax[i] = new Vector2f( (float)kImageGM[i].getMin(), (float)kImageGM[i].getMax() ); - - if ( !( bComputeLaplace && !m_kImage.isColorImage() ) ) - { - m_kVolumeGM[i] = VolumeImage.UpdateData(kImageGM[i], 0, null, m_kVolumeGM[i], m_kVolumeGMTarget, kImageName[i], true, false); - } - } - } - } - - - if ( bComputeLaplace && !m_kImage.isColorImage() ) - { - for (int i = 0; i < m_iTimeSteps; i++) { - final String kImageNameL = ModelImage.makeImageName(kImage.getFileInfo(0).getFileName(), new String( - "_Laplacian_" + i)); - ModelImage kImageGMGM = null; - kImageGMGM = ReadFromDisk(kImageNameL + ".xml", m_kDir); - if ( kImageGMGM != null && !checkImage(kImage, kImageGMGM ) ) - { - kImageGMGM.disposeLocal(); - kImageGMGM = null; - } - if (kImageGMGM == null) { - final JDialogLaplacian kCalcLaplacian = new JDialogLaplacian(null, m_akImages[i]); - kCalcLaplacian.setVisible(false); - kCalcLaplacian.setOutputNewImage(true); - kCalcLaplacian.setDisplayProgressBar(true); - kCalcLaplacian.setSeparateThread(false); - kCalcLaplacian.setUseOCL(true); - kCalcLaplacian.setSeparable(true); - kCalcLaplacian.actionPerformed(new ActionEvent(this, 0, "OK")); - kImageGMGM = kCalcLaplacian.getResultImage(); - } - if (kImageGMGM != null) { - m_kVolumeGM[i] = createGM_Laplace(kImageGM[i], kImageGMGM, m_kVolumeGM[i], i, true); - } else { - m_kVolumeGM[i] = VolumeImage.UpdateData(kImageGM[i], 0, null, m_kVolumeGM[i], m_kVolumeGMTarget, kImageName[i], true, false); - } - final ViewJFrameImage kImageFrame = ViewUserInterface.getReference().getFrameContainingImage(kImageGMGM); - if (kImageFrame != null) { - kImageFrame.close(); - } else if (kImageGMGM != null) { - kImageGMGM.disposeLocal(); - kImageGMGM = null; - } - } - } - - m_bGMInit = true; - m_kVolumeGMTarget.SetImage(m_kVolumeGM[0]); - m_kVolumeGMTarget.Reload(true); - - for ( int i = 0; i < kImageGM.length; i++ ) - { - if (kImageGM[i] != null) { - - kImageGM[i].setImageDirectory( m_kDir ); - kImageGM[i].setImageName( kImageName[i] + ".xml" ); - ModelImage.saveImage(kImageGM[i], kImageName[i] + ".xml", m_kDir ); - - - final ViewJFrameImage kImageFrame = ViewUserInterface.getReference().getFrameContainingImage(kImageGM[i]); - if (kImageFrame != null) { - kImageFrame.close(); - } - - kImageGM[i].disposeLocal(); - kImageGM[i] = null; - } - } - } - } - */ - - public static ModelImage getGradientMagnitude( ModelImage kImage, int i ) - { - if ( kImage == null ) - { - return null; - } - int index = kImage.getExtents()[2] / 2; - float xRes = kImage.getFileInfo(index).getResolutions()[0]; - float zRes = kImage.getFileInfo(index).getResolutions()[2]; - - float correction = xRes / zRes; - float[] sigmas = new float[]{1f,1f,correction}; - - int dimX = kImage.getExtents().length > 0 ? kImage.getExtents()[0] : 1; - int dimY = kImage.getExtents().length > 1 ? kImage.getExtents()[1] : 1; - int dimZ = kImage.getExtents().length > 2 ? kImage.getExtents()[2] : 1; - ModelImage outputImage = new ModelImage( kImage.getDataType(), new int[]{dimX,dimY,dimZ}, "temp" ); - AlgorithmGradientMagnitudeSep gradientMagAlgo = new AlgorithmGradientMagnitudeSep( kImage, sigmas, true, false ); -// OpenCLAlgorithmGradientMagnitude gradientMagAlgo = new OpenCLAlgorithmGradientMagnitude(outputImage, kImage, sigmas, -// true, true, false); - gradientMagAlgo.setRed(true); - gradientMagAlgo.setGreen(true); - gradientMagAlgo.setBlue(true); - gradientMagAlgo.setRunningInSeparateThread(false); - gradientMagAlgo.run(); - float[] resultBuffer = gradientMagAlgo.getResultBuffer(); - try { - outputImage.importData(0, resultBuffer, true); - } catch (IOException e) {} -// gradientMagAlgo.gradientMagnitudeSep3D( i ); - gradientMagAlgo.finalize(); - gradientMagAlgo = null; - return outputImage; - } - - -// private ModelImage getLaplace( ModelImage kImage, int i ) -// { -// String kImageName = ModelImage.makeImageName(kImage.getFileInfo(0).getFileName(), new String("_Laplacian_" + i)); -// ModelImage kImageL = ReadFromDisk(kImageName + ".xml", m_kDir); -// if ( kImageL != null && !checkImage(kImage, kImageL ) ) -// { -// kImageL.disposeLocal(); -// kImageL = null; -// } -// if (kImageL == null) { -// JDialogLaplacian kCalcLaplacian = new JDialogLaplacian(null, m_akImages[i]); -// kCalcLaplacian.setVisible(false); -// kCalcLaplacian.setOutputNewImage(true); -// kCalcLaplacian.setDisplayProgressBar(true); -// kCalcLaplacian.setSeparateThread(false); -// kCalcLaplacian.setUseOCL(true); -// kCalcLaplacian.setSeparable(true); -// kCalcLaplacian.actionPerformed(new ActionEvent(this, 0, "OK")); -// kImageL = kCalcLaplacian.getResultImage(); -// kCalcLaplacian = null; -// -// kImageL.setImageDirectory( m_kDir ); -// kImageL.setImageName( kImageName + ".xml" ); -// JDialogBase.updateFileInfo( kImage, kImageL ); -// ModelImage.saveImage(kImageL, kImageName + ".xml", m_kDir ); -// final ViewJFrameImage kImageFrame = ViewUserInterface.getReference().getFrameContainingImage(kImageL); -// if (kImageFrame != null) { -// kImageFrame.setVisible(false); -// } -// } -// return kImageL; -// } - - /** - * Initialize the VolumeImage with the ModelImage data. - * @param kProgress progress bar - * @param iProgress progress bar increment - */ - private void init(final ViewJProgressBar kProgress, final int iProgress, boolean initGradientMagnitude) { - // Create LUTS for the ModelImage: - initLUT(); - // Initialize Texture Maps: - if ( !m_kImage.isColorImage() ) - { - initImages(); - } - else - { - initImagesColor(); - } - if ( initGradientMagnitude ) - { - SetGradientMagnitude(null, true, m_kPostfix); - } - if (kProgress != null) { - kProgress.updateValueImmed(kProgress.getValue() + iProgress); - } - } - - /** - * Intializes the Textures and GraphicsImages used to render the ModelImage this - * VolumeImage represents. - */ - private void initImages() { - m_fDRRNormalize = computeIntegralNormalizationFactor(); - // Initialize Color Map GraphicsImage: - m_kColorMap = initColorMap(); - // Initialize Opacity Map for the GradientMagnitude image: - m_kOpacityMap_GM = InitOpacityMap(m_kImage, new String(m_kPostfix + "_GM")); - - final int iXBound = m_kImage.getExtents()[0]; - final int iYBound = m_kImage.getExtents()[1]; - final int iZBound = m_kImage.getExtents()[2]; - - /* - * Map the ModelImage volume data to a texture image, including for the ModelImage gradient magnitude data: - */ - final int[] aiExtents = m_kImage.getExtents(); - final int iNDims = aiExtents.length; - String kImageName; - GraphicsImage.FormatMode type = GraphicsImage.FormatMode.IT_RGBA8888 ; - - - if (iNDims == 3) { // ModelImage is 3D: - m_iTimeSteps = 1; - } - else { // ModelImage is 4D: - m_iTimeSteps = aiExtents[3]; - } - // Allocate a 3D GraphicsImage for each 3D Volume - m_kVolume = new GraphicsImage[m_iTimeSteps]; - m_kVolumeGM = new GraphicsImage[m_iTimeSteps]; - m_akGradientMagMinMax = new Vector2f[m_iTimeSteps]; - - final int[] aiSubset = new int[] {aiExtents[0], aiExtents[1], aiExtents[2]}; - - for (int i = 0; i < m_iTimeSteps; i++) { -// System.err.println( "initiImages : " + i ); - if ( m_iTimeSteps > 1 ) - { - // Will generate the ModelImage GraphicsImage representation and separate the 4D ModelImage into - // the 3D Subset image. - m_kVolume[i] = initVolumeData(m_kImage, i, m_kVolumeTarget, m_kImage.getImageName(), true, false); - } - else - { - // Already 3D, just generate the GraphicsImage: - m_kVolume[0] = initVolumeData(m_kImage, m_iTimeSlice, m_kVolumeTarget, m_kImage.getImageName(), true, false); - } - - // Allocate GraphcisImage for Gradient Magnitude Texture: - kImageName = ModelImage.makeImageName(m_kImage.getFileInfo(0).getFileName(), - new String("_GM_" + i)); - m_kVolumeGM[i] = new GraphicsImage(type, iXBound, iYBound, iZBound, - (byte[])null, kImageName); - - } - // Initialize the Gradient Magnitude Texture and set its GraphicsImage: - m_kVolumeGMTarget = new Texture(); - m_kVolumeGMTarget.SetImage(m_kVolumeGM[0]); - m_kVolumeGMTarget.SetShared(true); - m_kVolumeGMTarget.SetFilterType(Texture.FilterType.LINEAR); - m_kVolumeGMTarget.SetWrapType(0, Texture.WrapType.CLAMP_BORDER); - m_kVolumeGMTarget.SetWrapType(1, Texture.WrapType.CLAMP_BORDER); - m_kVolumeGMTarget.SetWrapType(2, Texture.WrapType.CLAMP_BORDER); - - // Initialize the ModelImage Texture and set its GraphicsImage: - m_kVolumeTarget = new Texture(); - m_kVolumeTarget.SetImage(m_kVolume[0]); - m_kVolumeTarget.SetShared(true); - m_kVolumeTarget.SetFilterType(Texture.FilterType.LINEAR); - m_kVolumeTarget.SetWrapType(0, Texture.WrapType.CLAMP_BORDER); - m_kVolumeTarget.SetWrapType(1, Texture.WrapType.CLAMP_BORDER); - m_kVolumeTarget.SetWrapType(2, Texture.WrapType.CLAMP_BORDER); - - // Initialize the ColorMap Texture and set its GraphicsImage: - m_kColorMapTarget = new Texture(); - m_kColorMapTarget.SetImage(m_kColorMap); - m_kColorMapTarget.SetShared(true); - - // Initialize the Normal Map Texture and set its GraphicsImage: - m_kScratchTarget = new Texture(); - m_kScratchTarget.SetImage(new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, - (byte[])null, "ScratchBuffer")); - m_kScratchTarget.SetShared(true); - m_kScratchTarget.SetFilterType(Texture.FilterType.LINEAR); - m_kScratchTarget.SetWrapType(0, Texture.WrapType.CLAMP_BORDER); - m_kScratchTarget.SetWrapType(1, Texture.WrapType.CLAMP_BORDER); - m_kScratchTarget.SetWrapType(2, Texture.WrapType.CLAMP_BORDER); - - // Initialize the Opacity Map for the Gradient Magnitude Texture and set its GraphicsImage: - m_kOpacityMapTarget_GM = new Texture(); - m_kOpacityMapTarget_GM.SetImage(m_kOpacityMap_GM); - m_kOpacityMapTarget_GM.SetShared(true); - - // Initialize the Surface Mask Texture and set its GraphicsImage: - m_kSurfaceImage = new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, - new byte[4* iXBound * iYBound * iZBound], "SurfaceImage"); - m_kSurfaceTarget = new Texture(); - m_kSurfaceTarget.SetImage(m_kSurfaceImage); - m_kSurfaceTarget.SetShared(true); - m_kSurfaceTarget.SetFilterType(Texture.FilterType.LINEAR); - m_kSurfaceTarget.SetWrapType(0, Texture.WrapType.CLAMP_BORDER); - m_kSurfaceTarget.SetWrapType(1, Texture.WrapType.CLAMP_BORDER); - m_kSurfaceTarget.SetWrapType(2, Texture.WrapType.CLAMP_BORDER); - - // Calculate the scale factors for rendering the volume with a unit cube: - InitScale(); - } - - - private void initImagesColor() { - m_fDRRNormalize = computeIntegralNormalizationFactor(); - // Initialize Color Map GraphicsImage: - m_kColorMap = initColorMap(); - // Initialize Opacity Map for the GradientMagnitude image: - m_kOpacityMap_GM = InitOpacityMap(m_kImage, new String(m_kPostfix + "_GM")); - - final int iXBound = m_kImage.getExtents()[0]; - final int iYBound = m_kImage.getExtents()[1]; - final int iZBound = m_kImage.getExtents()[2]; - - /* - * Map the ModelImage volume data to a texture image, including for the ModelImage gradient magnitude data: - */ - final int[] aiExtents = m_kImage.getExtents(); - final int iNDims = aiExtents.length; - String kImageName; - GraphicsImage.FormatMode type = GraphicsImage.FormatMode.IT_RGBA8888 ; - - - if (iNDims == 3) { // ModelImage is 3D: - m_iTimeSteps = 1; - } - else { // ModelImage is 4D: - m_iTimeSteps = aiExtents[3]; - } - // A 4D ModelImage is separated into the 3D Volumes: -// m_akImages = new ModelImage[m_iTimeSteps]; - // Allocate a 3D GraphicsImage for each 3D Volume - m_kVolume = new GraphicsImage[m_iTimeSteps]; - m_kVolumeGM = new GraphicsImage[m_iTimeSteps]; - m_kNormal = new GraphicsImage[m_iTimeSteps]; - m_akGradientMagMinMax = new Vector2f[m_iTimeSteps]; - - final int[] aiSubset = new int[] {aiExtents[0], aiExtents[1], aiExtents[2]}; - for (int i = 0; i < m_iTimeSteps; i++) { - - if ( m_iTimeSteps > 1 ) - { - // Will generate the ModelImage GraphicsImage representation and separate the 4D ModelImage into - // the 3D Subset image. -// m_akImages[i] = new ModelImage(m_kImage.getType(), aiSubset, JDialogBase.makeImageName(m_kImage -// .getImageName(), "_" + i)); -// JDialogBase.updateFileInfo( m_kImage, m_akImages[i] ); - m_kVolume[i] = initVolumeData(m_kImage, i, m_kVolumeTarget, m_kImage.getImageName(), true, false); -// m_akImages[i].copyFileTypeInfo(m_kImage); -// m_akImages[i].calcMinMax(); - } - else - { - // Already 3D, just generate the GraphicsImage: -// m_akImages[0] = m_kImage; - m_kVolume[0] = initVolumeData(m_kImage, m_iTimeSlice, m_kVolumeTarget, m_kImage.getImageName(), true, false); - } - // Allocate GraphcisImage for Normal Map Texture: - kImageName = ModelImage.makeImageName(m_kImage.getFileInfo(0).getFileName(), - new String("_Normal_" + i)); - m_kNormal[i] = new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, - (byte[])null, kImageName); - - // Allocate GraphcisImage for Gradient Magnitude Texture: - kImageName = ModelImage.makeImageName(m_kImage.getFileInfo(0).getFileName(), - new String("_GM_" + i)); - m_kVolumeGM[i] = new GraphicsImage(type, iXBound, iYBound, iZBound, - (byte[])null, kImageName); - } - // Initialize the Gradient Magnitude Texture and set its GraphicsImage: - m_kVolumeGMTarget = new Texture(); - m_kVolumeGMTarget.SetImage(m_kVolumeGM[0]); - m_kVolumeGMTarget.SetShared(true); - m_kVolumeGMTarget.SetFilterType(Texture.FilterType.LINEAR); - m_kVolumeGMTarget.SetWrapType(0, Texture.WrapType.CLAMP_BORDER); - m_kVolumeGMTarget.SetWrapType(1, Texture.WrapType.CLAMP_BORDER); - m_kVolumeGMTarget.SetWrapType(2, Texture.WrapType.CLAMP_BORDER); - - // Initialize the ModelImage Texture and set its GraphicsImage: - m_kVolumeTarget = new Texture(); - m_kVolumeTarget.SetImage(m_kVolume[0]); - m_kVolumeTarget.SetShared(true); - m_kVolumeTarget.SetFilterType(Texture.FilterType.LINEAR); - m_kVolumeTarget.SetWrapType(0, Texture.WrapType.CLAMP_BORDER); - m_kVolumeTarget.SetWrapType(1, Texture.WrapType.CLAMP_BORDER); - m_kVolumeTarget.SetWrapType(2, Texture.WrapType.CLAMP_BORDER); - - // Initialize the ColorMap Texture and set its GraphicsImage: - m_kColorMapTarget = new Texture(); - m_kColorMapTarget.SetImage(m_kColorMap); - m_kColorMapTarget.SetShared(true); - - // Initialize the Normal Map Texture and set its GraphicsImage: - m_kNormalMapTarget = new Texture(); - m_kNormalMapTarget.SetImage(m_kNormal[0]); - m_kNormalMapTarget.SetShared(true); - m_kNormalMapTarget.SetFilterType(Texture.FilterType.LINEAR); - m_kNormalMapTarget.SetWrapType(0, Texture.WrapType.CLAMP_BORDER); - m_kNormalMapTarget.SetWrapType(1, Texture.WrapType.CLAMP_BORDER); - m_kNormalMapTarget.SetWrapType(2, Texture.WrapType.CLAMP_BORDER); - - - // Initialize the Normal Map Texture and set its GraphicsImage: - m_kScratchTarget = new Texture(); - m_kScratchTarget.SetImage(new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, - (byte[])null, "ScratchBuffer")); - m_kScratchTarget.SetShared(true); - m_kScratchTarget.SetFilterType(Texture.FilterType.LINEAR); - m_kScratchTarget.SetWrapType(0, Texture.WrapType.CLAMP_BORDER); - m_kScratchTarget.SetWrapType(1, Texture.WrapType.CLAMP_BORDER); - m_kScratchTarget.SetWrapType(2, Texture.WrapType.CLAMP_BORDER); - - // Initialize the Opacity Map for the Gradient Magnitude Texture and set its GraphicsImage: - m_kOpacityMapTarget_GM = new Texture(); - m_kOpacityMapTarget_GM.SetImage(m_kOpacityMap_GM); - m_kOpacityMapTarget_GM.SetShared(true); - - // Initialize the Surface Mask Texture and set its GraphicsImage: - m_kSurfaceImage = new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, - new byte[4* iXBound * iYBound * iZBound], "SurfaceImage"); - m_kSurfaceTarget = new Texture(); - m_kSurfaceTarget.SetImage(m_kSurfaceImage); - m_kSurfaceTarget.SetShared(true); - m_kSurfaceTarget.SetFilterType(Texture.FilterType.LINEAR); - m_kSurfaceTarget.SetWrapType(0, Texture.WrapType.CLAMP_BORDER); - m_kSurfaceTarget.SetWrapType(1, Texture.WrapType.CLAMP_BORDER); - m_kSurfaceTarget.SetWrapType(2, Texture.WrapType.CLAMP_BORDER); - - // Calculate the scale factors for rendering the volume with a unit cube: - InitScale(); - } - - /** - * Create a new LUT for the input image. - * - * @param kImage ModelImage. - */ - private void initLUT() { - - if (m_kImage.isColorImage()) { - final float[] x = new float[4]; - final float[] y = new float[4]; - final Dimension dim = new Dimension(256, 256); - - // Set ModelRGB min max values; - x[0] = 0; - y[0] = dim.height - 1; - - x[1] = 255 * 0.333f; - y[1] = (dim.height - 1) - ( (dim.height - 1) / 3.0f); - - x[2] = 255 * 0.667f; - y[2] = (dim.height - 1) - ( (dim.height - 1) * 0.67f); - - x[3] = 255; - y[3] = 0; - - final int[] RGBExtents = new int[2]; - RGBExtents[0] = 4; - RGBExtents[1] = 256; - m_kRGBT = new ModelRGB(RGBExtents); - m_kRGBT.getRedFunction().importArrays(x, y, 4); - m_kRGBT.getGreenFunction().importArrays(x, y, 4); - m_kRGBT.getBlueFunction().importArrays(x, y, 4); - m_kRGBT.makeRGB( -1); - } else { - final int[] dimExtentsLUT = new int[2]; - - dimExtentsLUT[0] = 4; - dimExtentsLUT[1] = 256; - - m_kLUT = new ModelLUT(ModelLUT.GRAY, 256, dimExtentsLUT); - - float min, max; - - if (m_kImage.getType() == ModelStorageBase.UBYTE) { - min = 0; - max = 255; - } else if (m_kImage.getType() == ModelStorageBase.BYTE) { - min = -128; - max = 127; - } else { - min = (float) m_kImage.getMin(); - max = (float) m_kImage.getMax(); - } - - final float imgMin = (float) m_kImage.getMin(); - final float imgMax = (float) m_kImage.getMax(); - - m_kLUT.resetTransferLine(min, imgMin, max, imgMax); - } - } - - /** - * Initialize the scale factors. Based on the ModelImage Volume. - */ - private void InitScale() { - - int dimX = m_kImage.getExtents().length > 0 ? m_kImage.getExtents()[0] : 1; - int dimY = m_kImage.getExtents().length > 1 ? m_kImage.getExtents()[1] : 1; - int dimZ = m_kImage.getExtents().length > 2 ? m_kImage.getExtents()[2] : 1; - m_iMaxExtent = Math.max( dimX, Math.max( dimY, dimZ ) ); - - final float fMaxX = (m_kImage.getExtents()[0] - 1) * m_kImage.getFileInfo(0).getResolutions()[0]; - final float fMaxY = (m_kImage.getExtents()[1] - 1) * m_kImage.getFileInfo(0).getResolutions()[1]; - final float fMaxZ = (m_kImage.getExtents()[2] - 1) * m_kImage.getFileInfo(0).getResolutions()[2]; - - m_fMax = fMaxX; - if (fMaxY > m_fMax) { - m_fMax = fMaxY; - } - if (fMaxZ > m_fMax) { - m_fMax = fMaxZ; - } - m_fX = fMaxX / m_fMax; - m_fY = fMaxY / m_fMax; - m_fZ = fMaxZ / m_fMax; - } - - /** - * Reads an image from disk. - * - * @param kImageName image name - * @param kDir directory - * @return ModelImage - */ - private static ModelImage ReadFromDisk(final String kImageName, final String kDir) { - - final File kFile = new File(kDir, kImageName); - if ( !kFile.exists()) { - return null; - } - - final FileIO fileIO = new FileIO(); - return fileIO.readImage( kImageName, kDir ); - //return fileIO.readXML(kImageName + ".xml", kDir, false, false); - } - - private void readObject(final java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { - m_kDir = (String) in.readObject(); - if ( !m_kDir.equals("null")) { - final String kImageName = (String) in.readObject(); - m_kPostfix = (String) in.readObject(); - m_kImage = ReadFromDisk(kImageName, m_kDir); - init(null, 0, true); - } - } - - - /** - * Go to the next 3D volume sub-image for the 4D animation. - * Updates the Textures and causes them to be reloaded onto the GPU. - */ - private void update4D() { - if ( m_kImage == null ) return; - - m_kVolumeTarget.SetImage(m_kVolume[m_iTimeSlice]); - m_kVolumeTarget.Reload(true); - if ( m_bGMInit ) - { - if ( m_kVolumeGM[m_iTimeSlice] == null ) - { - ModelImage gmImage = getGradientMagnitude( m_kImage, m_iTimeSlice ); - m_akGradientMagMinMax[m_iTimeSlice] = new Vector2f( (float)gmImage.getMin(), (float)gmImage.getMax() ); - gmImage.disposeLocal(); - m_kVolumeGMTarget.SetImage(createGM_Laplace(gmImage, null, null, 0, true)); - m_kVolumeGMTarget.Reload(true); - } - else - { - m_kVolumeGMTarget.SetImage(m_kVolumeGM[m_iTimeSlice]); - m_kVolumeGMTarget.Reload(true); - } - } - if ( m_bNormalsInit && m_kImage.isColorImage() ) - { - m_kNormalMapTarget.SetImage(m_kNormal[m_iTimeSlice]); - m_kNormalMapTarget.Reload(true); - } - if ( m_bHistoInit && (m_kHisto[m_iTimeSlice] != null )) - { - m_kHistoTarget.SetImage(m_kHisto[m_iTimeSlice]); - m_kHistoTarget.Reload(true); - } - - m_kImage.setTimeSlice(m_iTimeSlice); - } - - /** - * Called when the opacity transfer function changes. This function updates the Texture - * and causes the data to be reloaded onto the GPU. - * - * @param kImage the ModelImage the transfer function applies to. - * @param kOpacityTexture the opacity Texture passed to the GPU - * @param kOpacityMap the opacity data stored in the GraphicsImage - * @param kTransfer the new transfer function. - */ - private boolean UpdateImages(final ModelImage kImage, final Texture kOpacityTexture, - final GraphicsImage kOpacityMap, final TransferFunction kTransfer) { - final int iLutHeight = 256; - final float[] afData = kOpacityMap.GetFloatData(); - - final float fRange = (float) (kImage.getMax() - kImage.getMin()); - final float fStep = fRange / iLutHeight; - float fDataValue = (float) kImage.getMin(); - for (int i = 0; i < iLutHeight; i++) { - afData[i] = (kTransfer.getRemappedValue(fDataValue, iLutHeight) / 255.0f); - fDataValue += fStep; - } - kOpacityTexture.Reload(true); - return true; - } - - public TransferFunction getOpacityFn() { - return opacityTransferFn; - } - - /** - * Update the opacity transfer function. - * - * @param kImage the ModelImage the transfer function applies to. - * @param kOpacityTexture the opacity Texture passed to the GPU - * @param kOpacityMap the opacity data stored in the GraphicsImage - * @param kTransfer the new transfer function. - */ - private boolean UpdateImages2(final ModelImage kImage, final Texture kOpacityTexture, - final GraphicsImage kOpacityMap, final TransferFunction kTransfer) { - opacityTransferFn = new TransferFunction(kTransfer); - final int iLutHeight = kOpacityMap.GetBound(0); - final byte[] abData = kOpacityMap.GetData(); - - final float fRange = (float) (kImage.getMax() - kImage.getMin()); - final float fStep = fRange / iLutHeight; - float fDataValue = (float) kImage.getMin(); - float fVal; - for (int i = 0; i < iLutHeight; i++) { - fVal = (kTransfer.getRemappedValue(fDataValue, iLutHeight) / 255.0f); - abData[i * 4 + 3] = (byte) (fVal * 255); - fDataValue += fStep; - } - kOpacityTexture.Reload(true); - return true; - } - - private void writeObject(final java.io.ObjectOutputStream out) throws IOException { - if (m_kImage != null) { - out.writeObject(m_kDir); - out.writeObject(m_kImage.getImageFileName()); - out.writeObject(m_kPostfix); - m_kImage.saveImage(m_kDir, m_kImage.getImageFileName(), FileUtility.XML, false, false); - } else { - out.writeObject("null"); - } - } -} +package gov.nih.mipav.view.renderer.WildMagic.Render; + + +import static java.lang.System.nanoTime; +import static java.lang.System.out; +import gov.nih.mipav.model.algorithms.filters.AlgorithmGradientMagnitudeSep; +import gov.nih.mipav.model.algorithms.filters.OpenCL.filters.OpenCLAlgorithmGradientMagnitude; +import gov.nih.mipav.model.algorithms.filters.OpenCL.filters.OpenCLAlgorithmVolumeNormals; +import gov.nih.mipav.model.file.*; +import gov.nih.mipav.model.structures.*; + +import gov.nih.mipav.view.*; +import gov.nih.mipav.view.dialogs.*; +import gov.nih.mipav.view.renderer.WildMagic.VolumeTriPlanarInterface; + +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.io.*; +import java.nio.Buffer; +import java.util.BitSet; +import java.util.Vector; + + +import org.jocl.CL; + +import WildMagic.LibFoundation.Mathematics.ColorRGB; +import WildMagic.LibFoundation.Mathematics.ColorRGBA; +import WildMagic.LibFoundation.Mathematics.Vector2f; +import WildMagic.LibGraphics.Rendering.*; + +/** * + * The VolumeImage class provides an interface between the MIPAV ModelImage and the 2D and 3D Textures used to render + * the ModelImage on the GPU. The VolumeImage creates the supporting Texture and GraphicImage objects that pass + * the ModelImage data to the GPU. It also creates Texture and GraphicsImage objects for the ModelImage Look-up Table (LUT) + * and the ModelImage opacity transfer function. Other textures that are used for advanced volume rendering, such as + * a normal map for surface rendering, and the gradient-magnitude and laplace images for the multi-histogram rendering are + * calculated and passed on-demand to the GPU when the user selects these options in the Volume-Renderer user-interface. + * + * The VolumeImage data structure handles all GPU content for rendering one ModelImage. All Textures and GraphicsImages + * are initialized and stored in the VolumeImage. When needed by the renderer they are loaded onto the GPU. Any images + * that are derived from the ModelImage: Normal map, Gradient Magnitude, Laplace, are either read from file or calculated + * and then written to file. The supporting files are stored in a directory on disk located next to the original ModelImage. + * The directory is named with the ModelImage name followed by "_RenderFiles". + * + * + */ +public class VolumeImage implements Serializable { + /** */ + private static final long serialVersionUID = -7254697711265907746L; + + /** Reference to ModelImage image */ + private ModelImage m_kImage; + private ModelImage m_kImageGM; + + /** GraphicsImage contains GM opacity transfer function data: */ + private GraphicsImage m_kOpacityMap_GM = null; + + /** + * Texture contains texture filter modes and GraphicsImage for opacity transfer function: + */ + private Texture m_kOpacityMapTarget_GM = null; + + /** Data storage for volume: */ + private GraphicsImage[] m_kVolume; + + /** Texture object for data: */ + private Texture m_kVolumeTarget; + + /** Data storage for normals: */ + private GraphicsImage[] m_kNormal; + /** Set to true if the Normal Map has been initialized. */ + private boolean m_bNormalsInit = false; + + /** Texture object for normal map: */ + private Texture m_kNormalMapTarget; + + /** Texture object for GPU computations: */ + private Texture m_kScratchTarget; + + /** Data storage for color map: */ + private GraphicsImage m_kColorMap; + + /** Texture object for color map: */ + private Texture m_kColorMapTarget; + + /** Data storage for volume gradient magnitude: */ + private GraphicsImage[] m_kVolumeGM; + /** Set to true if the Gradient Magnitude texture map has been initialized. */ + private boolean m_bGMInit = false; + + /** Texture object for volume gradient magnitude data: */ + private Texture m_kVolumeGMTarget; + + /** Data storage for surfaces: */ + private GraphicsImage m_kSurfaceImage; + + /** Texture object for surfaces: */ + private Texture m_kSurfaceTarget; + + /** ModelLUT */ + private ModelLUT m_kLUT = null; + + /** ModelRGB */ + private ModelRGB m_kRGBT = null; + + /** Image scale factors for display in 3D */ + private float m_fX = 1, m_fY = 1, m_fZ = 1, m_fMax = 1; + private int m_iMaxExtent = 1; + + /** Image name post-fix typically either 'A' or 'B' */ + private String m_kPostfix = null; + + /** Directory for calculated images */ + private String m_kDir = null; + + /** Histogram data for multi-histogram interface */ + private GraphicsImage[] m_kHisto = null; + /** Set to true when the multi-histogram histogram texture has been initialized. */ + private boolean m_bHistoInit = false; + + /** Texture object for data: */ + private Texture m_kHistoTarget; + + /** Texture coordinates for displaying histogram in 2D */ + private Vector2f[] m_akHistoTCoord = null; + + private float m_fDRRNormalize = 255.0f; + + /** Current position in time (4D data) */ + private int m_iTimeSlice = 0; + + /** Total number of time-slices (4D data) */ + private int m_iTimeSteps = 0; + + /** 3D sub-images (4D data) */ +// private ModelImage[] m_akImages; +// private ModelImage[] m_akImagesGM; + + private Vector2f[] m_akGradientMagMinMax; + + private TransferFunction opacityTransferFn; + + /* Default Constructor */ + public VolumeImage() {} + + /** + * Create a Volume image with the input ModelImage. The supporting images for advanced volume rendering, such as + * the normal map, gradient magnitude and laplace images are generated on-demand and stored in a directory for + * later use. The directory is created if it does not already exist, with the ModelImage name + "_RenderFiles" as + * the directory name. + * + * @param bClone, when true clone the input ModelImage, when false reference the ModelImage + * @param kImage input ModelImage + * @param kPostfix Postfix for images 'A' or 'B' + * @param kProgress progress bar + * @param iProgress progress bar increment + */ + public VolumeImage(boolean bClone, final ModelImage kImage, final String kPostfix, final ViewJProgressBar kProgress, final int iProgress) { + this( bClone, kImage, kPostfix, kProgress, iProgress, true ); + } + + public VolumeImage(boolean bClone, final ModelImage kImage, final String kPostfix, final ViewJProgressBar kProgress, final int iProgress, boolean initGradientMagnitude) { + m_kPostfix = new String(kPostfix); + // clone the input image, in the future this might be a reference. + if ( bClone ) + { + m_kImage = (ModelImage)kImage.clone(); + } + else + { + m_kImage = kImage; + } + // Initialize the Texture maps. + init(kProgress, iProgress, initGradientMagnitude); + } + + /** + * Copy the data from the input GraphicsImage and return a new ModelImage of that data. + * Any changes to the GraphicsImage that occur only on the GPU can first be written from + * the GPU back into the GraphicsImage CPU data storage. This enables calculations that + * are performed on the GPU to be written back into a ModelImage data structure. + * + * @param kImage Graphics Image to copy + * @param bSwap when true convert from RGBA (graphics format) to ARGB (ModelImage format) + * @return new ModelImage from Volume Texture on GPU. + */ + public static ModelImage CreateImageFromTexture(final GraphicsImage kImage, final boolean bSwap) { + final int iXBound = kImage.GetBound(0); + final int iYBound = kImage.GetBound(1); + final int iZBound = kImage.GetBound(2); + final int iSize = iXBound * iYBound * iZBound; + final int[] extents = new int[] {iXBound, iYBound, iZBound}; + + ModelImage kResult = null; + if (kImage.GetFormat() == GraphicsImage.FormatMode.IT_RGBA8888) { + byte[] aucData = kImage.GetData(); + if (bSwap) { + byte bVal = 0; + aucData = new byte[4 * iXBound * iYBound * iZBound]; + for (int i = 0; i < iSize; i += 4) { + if (kImage.GetData()[i + 1] > bVal) { + bVal = kImage.GetData()[i + 1]; + } + aucData[i] = kImage.GetData()[i + 3]; + aucData[i + 1] = kImage.GetData()[i + 1]; + aucData[i + 2] = kImage.GetData()[i + 2]; + aucData[i + 3] = kImage.GetData()[i]; + //System.err.println( kImage.GetData()[i + 3] + " " + kImage.GetData()[i + 1] + " " + kImage.GetData()[i + 2] ); + } + // System.err.println( bVal ); + } + try { + kResult = new ModelImage(ModelStorageBase.ARGB, extents, ""); + kResult.importData(0, aucData, true); + } catch (final IOException e) { + e.printStackTrace(); + } + } else { + final byte[] aiImageData = kImage.GetData(); + try { + kResult = new ModelImage(ModelStorageBase.UBYTE, extents, ""); + kResult.importData(0, aiImageData, true); + } catch (final IOException e) { + e.printStackTrace(); + } + } + return kResult; + } + + /** + * Initialize the textures for the color lookup table. + * + * @param kLUT the new LUT. + * @param kRGBT the new RGB table. + * @param kPostfix the string postfix to concatenate to the "ColorMap" image name. + * @return GraphicsImage, the new GraphicsImage storing the colormap lookup table. + */ + public static GraphicsImage InitColorMap( Texture kTexture, GraphicsImage kImage, final ModelStorageBase kLUT, final String kPostFix) { + byte[] aucData; + if ( kImage == null ) + { + aucData = new byte[256 * 4]; + } + else + { + aucData = kImage.GetData(); + } + if (kLUT instanceof ModelLUT ) { + // ModelImage is Color, initialize the ModelRGB + ModelLUT.exportIndexedLUTMin((ModelLUT)kLUT, aucData); + } + else if (kLUT instanceof ModelRGB ) { + // Initialize the ModelLUT + ModelLUT.exportIndexedLUTMin((ModelRGB)kLUT, aucData); + } + if ( kImage == null ) + { + // Return the new GraphicsImage containing the table data: + return new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, 256, aucData, new String("ColorMap" + kPostFix)); + } + if ( kTexture != null ) + { + kTexture.Reload(true); + } + return kImage; + } + + + private GraphicsImage initColorMap() { + final byte[] aucData = new byte[256 * 4]; + if (m_kRGBT != null) + { + // ModelImage is Color, initialize the ModelRGB + ModelLUT.exportIndexedLUTMin(m_kRGBT, aucData); + } else if ( m_kLUT != null ) + { + // Initialize the ModelLUT + ModelLUT.exportIndexedLUTMin(m_kLUT, aucData); + } + // Return the new GraphicsImage containing the table data: + return new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, 256, 1, aucData, new String("ColorMap" + m_kImage.getImageName() + m_kPostfix)); + } + + + /** + * When a ModelImage changes on the CPU, this function is used to update the ModelImage + * data on the CPU. + * + * @param kImage Modified ModelImage to copy into the GPU Texture and GraphicsImage + * @param iTimeSlice time value for 4D image, 0 otherwise + * @param kNewImage a new ModelImage (always 3D) that the data or data subset for 4D image can be copied into (when non-null). + * @param kVolumeImage GraphicsImage that will hold the ModelImage data + * @param kVolumeTexture Texture object containing the GraphicsImage + * @param kImageName new image name for the new ModelImage. + * @param bSwap when true swap the ARGB (ModelImage) color data representation to a RGBA (GPU) color representation. + * @return + */ + public static GraphicsImage UpdateData(final ModelImage kImage, final int iTimeSlice, final ModelImage kNewImage, + final GraphicsImage kVolumeImage, final Texture kVolumeTexture, final String kImageName, + final boolean bSwap, final boolean bRescale) { + GraphicsImage kReturn = kVolumeImage; + final int iXBound = kImage.getExtents()[0]; + final int iYBound = kImage.getExtents()[1]; + final int iZBound = kImage.getExtents()[2]; + + byte[] aucData = null; + int iSize = iXBound * iYBound * iZBound; + if (kImage.isColorImage()) { + iSize *= 4; + aucData = new byte[iSize]; + try { + kImage.exportDataUseMask(iTimeSlice * iSize, iSize, bRescale, aucData); + if (bSwap) { + for (int i = 0; i < iSize; i += 4) { + final byte tmp = aucData[i]; + aucData[i] = aucData[i + 1]; + aucData[i + 1] = aucData[i + 2]; + aucData[i + 2] = aucData[i + 3]; + aucData[i + 3] = tmp; + } + } + } catch (final IOException e) { + e.printStackTrace(); + } + if (kReturn == null) { + kReturn = new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, aucData, + kImageName); + } else { + kReturn.SetData(aucData, iXBound, iYBound, iZBound); + } + } else { + aucData = new byte[iSize]; + try { + kImage.exportDataUseMask(iTimeSlice * iSize, iSize, bRescale, aucData); + // Temporary make the texture an RGBA texture until JOGL2 fixes NPOT textures. + byte[] aucData2 = new byte[iSize*4]; + for (int i = 0; i < iSize; i++) { + aucData2[i * 4 + 0] = aucData[i]; + aucData2[i * 4 + 1] = aucData[i]; + aucData2[i * 4 + 2] = aucData[i]; + aucData2[i * 4 + 3] = 1; + } + + if (kReturn == null) { + kReturn = new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, aucData2, + kImageName); + } else { + kReturn.SetData(aucData2, iXBound, iYBound, iZBound); + } + } catch (final IOException e) { + e.printStackTrace(); + } + + } + if (kNewImage != null) { + try { + kNewImage.importData(0, aucData, true); + } catch (final IOException e) {} + } + if (kVolumeTexture != null) { + kVolumeTexture.Reload(true); + } + return kReturn; + } + + private GraphicsImage initVolumeData(final ModelImage kImage, final int iTimeSlice, + final Texture kVolumeTexture, final String kImageName, final boolean bSwap, final boolean bRescale) + { + GraphicsImage kReturn = null; + final int iXBound = kImage.getExtents()[0]; + final int iYBound = kImage.getExtents()[1]; + final int iZBound = kImage.getExtents()[2]; + + byte[] aucData = null; + int iSize = iXBound * iYBound * iZBound; + if (kImage.isColorImage()) { + iSize *= 4; + aucData = new byte[iSize]; + try { + kImage.exportDataUseMask(iTimeSlice * iSize, iSize, bRescale, aucData); + if (bSwap) { + for (int i = 0; i < iSize; i += 4) { + final byte tmp = aucData[i]; + aucData[i] = aucData[i + 1]; + aucData[i + 1] = aucData[i + 2]; + aucData[i + 2] = aucData[i + 3]; + aucData[i + 3] = tmp; + } + } + } catch (final IOException e) { + e.printStackTrace(); + } + kReturn = new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, aucData, + kImageName); + } else { + aucData = new byte[iSize]; + try { + kImage.exportDataUseMask(iTimeSlice * iSize, iSize, bRescale, aucData); + // Temporary make the texture an RGBA texture until JOGL2 fixes NPOT textures. + byte[] aucData2 = new byte[iSize*4]; + for (int i = 0; i < iSize; i++) { + aucData2[i * 4 + 0] = aucData[i]; + aucData2[i * 4 + 1] = aucData[i]; + aucData2[i * 4 + 2] = aucData[i]; + aucData2[i * 4 + 3] = 1; + } + + kReturn = new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, aucData2, + kImageName); + } catch (final IOException e) { + e.printStackTrace(); + } + + } + if (kVolumeTexture != null) { + kVolumeTexture.Reload(true); + } + return kReturn; + } + + private GraphicsImage resetVolumeData(final ModelImage kImage, final int iTimeSlice, GraphicsImage kGraphicsImage, + final Texture kVolumeTexture, final String kImageName, final boolean bSwap, final boolean bRescale) + { + final int iXBound = kImage.getExtents()[0]; + final int iYBound = kImage.getExtents()[1]; + final int iZBound = kImage.getExtents()[2]; + int iSize = iXBound * iYBound * iZBound; + + byte[] aucData = null; + if (kImage.isColorImage()) { + iSize *= 4; + aucData = kGraphicsImage.GetData(); + try { + kImage.exportDataUseMask(iTimeSlice * iSize, iSize, bRescale, aucData); + if (bSwap) { + for (int i = 0; i < iSize; i += 4) { + final byte tmp = aucData[i]; + aucData[i] = aucData[i + 1]; + aucData[i + 1] = aucData[i + 2]; + aucData[i + 2] = aucData[i + 3]; + aucData[i + 3] = tmp; + } + } + } catch (final IOException e) { + e.printStackTrace(); + } + } + else { + aucData = new byte[iSize]; + try { + kImage.exportDataUseMask(iTimeSlice * iSize, iSize, bRescale, aucData); + byte[] aucData2 = kGraphicsImage.GetData(); + for (int i = 0; i < iSize; i++) { + aucData2[i * 4 + 0] = aucData[i]; + aucData2[i * 4 + 1] = aucData[i]; + aucData2[i * 4 + 2] = aucData[i]; + aucData2[i * 4 + 3] = 1; + } + } catch (final IOException e) { + e.printStackTrace(); + } + + } + if (kVolumeTexture != null) { + kVolumeTexture.Reload(true); + } + return kGraphicsImage; + } + + private void addNormals(final ModelImage kImage, final int iTimeSlice) { + final int iXBound = kImage.getExtents()[0]; + final int iYBound = kImage.getExtents()[1]; + final int iZBound = kImage.getExtents()[2]; + + int iSize = iXBound * iYBound * iZBound; + byte[] aucData = new byte[iSize * 4]; + try { + kImage.exportDataUseMask(0, iSize * 4, true, aucData); + byte[] volumeData = m_kVolume[iTimeSlice].GetData(); + for (int i = 0; i < iSize; i++) { + volumeData[i*4 + 1] = aucData[i*4 + 1]; + volumeData[i*4 + 2] = aucData[i*4 + 2]; + volumeData[i*4 + 3] = aucData[i*4 + 3]; + } + } catch (final IOException e) { + e.printStackTrace(); + } + m_kVolumeTarget.Reload(true); + } + + + private GraphicsImage createGM_Laplace(final ModelImage kImageGM, final ModelImage kImageL, + final GraphicsImage kVolumeImage, + final int iTimeSlice, final boolean bSwap) { + + GraphicsImage kReturn = kVolumeImage; + final int iXBound = kImageGM.getExtents()[0]; + final int iYBound = kImageGM.getExtents()[1]; + final int iZBound = kImageGM.getExtents()[2]; + + int iSize = iXBound * iYBound * iZBound; + byte[] aucDataL = new byte[iSize]; + + if ( kImageL != null ) + { + try { + kImageL.exportDataUseMask(0, iSize, false, aucDataL); + } catch (final IOException e) { + e.printStackTrace(); + } + } + + byte[] aucDataGM = null; + if (kImageGM.isColorImage()) { + iSize *= 4; + aucDataGM = new byte[iSize]; + try { + kImageGM.exportDataUseMask(0, iSize, false, aucDataGM); + if (bSwap) { + for (int i = 0, j = 0; i < iSize; i += 4) { + aucDataGM[i] = aucDataGM[i + 1]; + aucDataGM[i + 1] = aucDataGM[i + 2]; + aucDataGM[i + 2] = aucDataGM[i + 3]; + if ( kImageL != null ) + { + aucDataGM[i + 3] = aucDataL[j++]; + } + } + } + kImageGM.importData( 0, aucDataGM, false ); + } catch (final IOException e) { + e.printStackTrace(); + } + if (kReturn == null) { + kReturn = new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, aucDataGM, + kImageGM.getImageName()); + } else { + kReturn.SetData(aucDataGM, iXBound, iYBound, iZBound); + } + } + else + { + try { + aucDataGM = new byte[iSize]; + kImageGM.exportDataUseMask(0, iSize, false, aucDataGM); + byte[] aucData2 = new byte[iSize*4]; + for (int i = 0; i < iSize; i++) { + aucData2[i * 4 + 0] = aucDataGM[i]; + aucData2[i * 4 + 1] = aucDataGM[i]; + aucData2[i * 4 + 2] = aucDataGM[i]; + if ( kImageL != null ) + { + aucData2[i * 4 + 3] = aucDataL[i]; + } + } + + if (kReturn == null) { + kReturn = new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, aucData2, + kImageGM.getImageName()); + } else { + kReturn.SetData(aucData2, iXBound, iYBound, iZBound); + } + } catch (final IOException e) { + e.printStackTrace(); + } catch ( java.lang.OutOfMemoryError e ) { + return null; + } + aucDataGM = null; + } + return kReturn; + } + + + + /** + * Creates a new GraphicsImage for the input ModelSimpleImage. The ModelSimpleImage data is + * referenced by the new GraphicsImage and will be passed to the GPU as a texture. + * @param kImage input ModelSimpleImage. + * @param kImageName name for the GraphicsImage. + * @return a new GraphcisImage. + */ + public static GraphicsImage UpdateData(final ModelSimpleImage kImage, final String kImageName) { + final GraphicsImage.FormatMode eType = GraphicsImage.FormatMode.IT_L32F; + + if (kImage.nDims == 3) { + return new GraphicsImage(eType, kImage.extents[0], kImage.extents[1], kImage.extents[2], kImage.data, + kImageName); + } + return new GraphicsImage(eType, kImage.extents[0], kImage.extents[1], 1, kImage.data, kImageName); + } + + /** + * When the LUT changes, this function updates the LUT data on the GPU. + * + * @param kColorTexture the color-map Texture object. + * @param kColorMap the color-map GraphicsImage object (stores data). + * @param kLUT the updated or new LUT. + */ + public static void UpdateImages(final Texture kColorTexture, final GraphicsImage kColorMap, final ModelLUT kLUT) { + if (kLUT == null) { + return; + } + ModelLUT.exportIndexedLUTMin(kLUT, kColorMap.GetData()); + kColorTexture.Reload(true); + } + + /** + * When the ModelImage data is rendered as a solid surface, the Normal map is used in the rendering. + * The Normal map is calculated on the GPU by one of the GLSL shader programs. This function is called + * after the GPU calculation has finished and the GPU data has been copied into a new ModelImage on the CPU + * the new ModelImage then contains the Normal map information, which is written into a file and + * copied into the Normal map GraphicsImage used to render the original ModelImage. + * + * @param i current 3D sub-image for 4D data. If the data is 3D this value should be 0. + * @param kImage a new ModelImage containing the calculated Normals. + */ + public void CopyNormalFiles(final int i, final ModelImage kImage) { + final String kImageName = ModelImage.makeImageName(m_kImage.getFileInfo(0).getFileName(), "_Normal_" + + i); + JDialogBase.updateFileInfo( m_kImage, kImage ); + ModelImage.saveImage( kImage, kImageName + ".xml", m_kDir, false ); + if ( m_kImage.isColorImage() ) + { + m_kNormal[i] = initVolumeData(kImage, 0, m_kNormalMapTarget, kImage.getImageName(), true, true); + } + else + { + addNormals(kImage, i); + } + } + + /** + * Read the current Volume Texture from the GPU and return a new ModelImage of that data. + * + * @return new ModelImage from Volume Texture on GPU. + */ + public static ModelImage CreateImageFromTexture(final GraphicsImage kImage) { + final int[] extents = new int[ kImage.GetDimension() ]; + for ( int i = 0; i < extents.length; i++ ) + { + extents[i] = kImage.GetBound(i); + } + + //GraphicsImage.Type eType = kImage.GetType(); + int type = ModelStorageBase.ARGB; + /* + switch ( eType ) + { + case IT_BYTE : type = ModelStorageBase.BYTE; break; + case IT_UBYTE : type = ModelStorageBase.UBYTE; break; + case IT_SHORT : type = ModelStorageBase.SHORT; break; + case IT_USHORT : type = ModelStorageBase.USHORT; break; + case IT_INT : type = ModelStorageBase.INTEGER; break; + case IT_UINT : type = ModelStorageBase.UINTEGER; break; + case IT_LONG : type = ModelStorageBase.LONG; break; + case IT_FLOAT : type = ModelStorageBase.FLOAT; break; + case IT_DOUBLE : type = ModelStorageBase.DOUBLE; break; + } + */ + final ModelImage kResult = new ModelImage(type, extents, kImage.GetName() ); + int size = kImage.GetQuantity(); + for ( int i = 0; i < size;i++ ) + { + kResult.set(i*4 + 1, kImage.GetData()[i*3 + 0]); + kResult.set(i*4 + 2, kImage.GetData()[i*3 + 1]); + kResult.set(i*4 + 3, kImage.GetData()[i*3 + 2]); + } + return kResult; + } + + /** + * Memory cleanup. + */ + public void dispose() { + if (m_kImage == null) { + return; + } + m_kImage.disposeLocal(); + m_kImage = null; + + for (final GraphicsImage element : m_kVolume) { + element.dispose(); + } + m_kVolume = null; + m_kVolumeTarget.dispose(); + m_kVolumeTarget = null; + + if ( m_kNormal != null ) + { + for (final GraphicsImage element : m_kNormal) { + element.dispose(); + } + m_kNormal = null; + } + if ( m_kNormalMapTarget != null ) + { + m_kNormalMapTarget.dispose(); + m_kNormalMapTarget = null; + } + + m_kScratchTarget.dispose(); + m_kScratchTarget = null; + + m_kColorMap.dispose(); + m_kColorMap = null; + m_kColorMapTarget.dispose(); + m_kColorMapTarget = null; + + if ( m_kImageGM != null ) + { + m_kImageGM.disposeLocal(); + m_kImageGM = null; + } + + for (final GraphicsImage element : m_kVolumeGM) { + if ( element != null ) + element.dispose(); + } + m_kVolumeGM = null; + m_kVolumeGMTarget.dispose(); + m_kVolumeGMTarget = null; + + + m_kOpacityMap_GM.dispose(); + m_kOpacityMap_GM = null; + m_kOpacityMapTarget_GM.dispose(); + m_kOpacityMapTarget_GM = null; + + if (m_kSurfaceImage != null) { + m_kSurfaceImage.dispose(); + m_kSurfaceImage = null; + m_kSurfaceTarget.dispose(); + m_kSurfaceTarget = null; + } + + m_kLUT = null; + m_kPostfix = null; + if ( m_kHisto != null ) + { + for (final GraphicsImage element : m_kHisto) { + if ( element != null ) + { + element.dispose(); + } + } + m_kHisto = null; + } + m_akHistoTCoord = null; + } + + + /** + * This function is called when the user selects the Surface or Composite Surface volume rendering option. + * If the normals have already been initialized the function returns. Otherwise the function checks if the + * normals are available in a file on disk, and if so if they match the parameters (size, units, resolutions) of + * the original ModelImage. If the files match they are used and the Normal map is read from file. Otherwise this + * function launches the GPU-based Normal calculation. That calculation when finished calls the CopyNormalFiles + * which writes the calculated normals to disk and updates the Normal map on the GPU for rendering. + */ + public void GenerateNormalFiles( VolumeTriPlanarInterface parentFrame ) { + if ( m_bNormalsInit ) + { + return; + } + if ( !m_bNormalsInit ) + { + int dimX = m_kImage.getExtents().length > 0 ? m_kImage.getExtents()[0] : 1; + int dimY = m_kImage.getExtents().length > 1 ? m_kImage.getExtents()[1] : 1; + int dimZ = m_kImage.getExtents().length > 2 ? m_kImage.getExtents()[2] : 1; + ModelImage outputImage = new ModelImage( ModelStorageBase.ARGB_FLOAT, new int[]{dimX,dimY,dimZ}, "temp" ); + for (int i = 0; i < m_iTimeSteps; i++) { + OpenCLAlgorithmVolumeNormals oclNormals = new OpenCLAlgorithmVolumeNormals( m_kImage, outputImage, CL.CL_DEVICE_TYPE_GPU ); + oclNormals.setTime(i); + oclNormals.run(); + if ( m_kImage.isColorImage() ) + { + m_kNormal[i] = initVolumeData(outputImage, 0, m_kNormalMapTarget, outputImage.getImageName(), true, true); + } + else + { + addNormals(outputImage, i); + } + } + m_bNormalsInit = true; + + if ( m_kNormal != null) + { + m_kNormalMapTarget.SetImage(m_kNormal[m_iTimeSlice]); + m_kNormalMapTarget.Reload(true); + } + outputImage.disposeLocal(); + } + } + + /** + * Return the Color Map Texture. + * @return Volume color map Texture. + */ + public Texture GetColorMapTarget() { + return m_kColorMapTarget; + } + + /** + * Return the normalization factor for DDR rendering mode. + * @return normalization factor for DDR rendering mode. + */ + public float getDRRNorm() { + return m_fDRRNormalize; + } + + /** + * Return the Gradient Magnitude Texture. + * @return Gradient Magnitude Texture. + */ + public Texture GetGradientMapTarget() { + return m_kVolumeGMTarget; + } + + /** + * Returns true if the multi-histogram histogram texture has been initialized, false otherwise. + * @return true if the multi-histogram histogram texture has been initialized, false otherwise. + */ + public boolean isHistoInit() + { + return m_bHistoInit; + } + + /** + * Returns the multi-histogram histogram Texture. + * @return the multi-histogram histogram Texture. + */ + public Texture GetHistoTarget() { + return m_kHistoTarget; + } + + +// private ModelImage[] m_akHistogram; +// public ModelImage GetHistogram() { +// +// if ( !m_bHistoInit ) +// { +// SetGradientMagnitude(null, true, m_kPostfix); +// } +// if ( m_akHistogram == null ) +// { +// m_akHistogram = new ModelImage[m_akImages.length]; +// } +// if ( m_akHistogram[m_iTimeSlice] == null ) +// { +// m_akHistogram[m_iTimeSlice] = new ModelImage(ModelStorageBase.INTEGER, new int[]{256,256}, "JointHisto" + m_iTimeSlice); +// try { +// m_akHistogram[m_iTimeSlice].importData(m_kHisto[m_iTimeSlice].GetData()); +// } catch (IOException e) { +// e.printStackTrace(); +// } +// } +// return m_akHistogram[m_iTimeSlice]; +// } + + /** + * Return the texture coordinates for the multi-histogram histogram texture. + * @return the texture coordinates for the multi-histogram histogram texture. + */ + public Vector2f[] GetHistoTCoords() { + return m_akHistoTCoord; + } + + public ModelImage GetGradientMagnitudeImage() + { + return m_kImageGM; + } + + public Vector2f GetGradientMagnitudeMinMax() + { + return m_akGradientMagMinMax[m_iTimeSlice]; + } + + public float GetGradientMagnitudeMin() + { + return m_akGradientMagMinMax[m_iTimeSlice].X; + } + + public float GetGradientMagnitudeMax() + { + return m_akGradientMagMinMax[m_iTimeSlice].Y; + } + + /** + * Return the ModelImage volume data. + * @return ModelImage volume data. + */ + public ModelImage GetImage() { + return m_kImage; + } + + /** + * Return the ModelImage LUT. + * @return Volume LUT. + */ + public ModelLUT GetLUT() { + return m_kLUT; + } + + /** + * Return the Normal map Texture. + * @return Normal map Texture. + */ + public Texture GetNormalMapTarget() { + return m_kNormalMapTarget; + } + + public Texture GetScratchTarget() { + return m_kScratchTarget; + } + + /** + * Return the gradient magnitude opacity transfer function Texture. + * @return gradient magnitude opacity transfer function Texture. + */ + public Texture GetOpacityMapGMTarget() { + return m_kOpacityMapTarget_GM; + } + + /** + * Return the postfix for this VolumeImage. + * @return postfix for this VolumeImage. + */ + public String GetPostfix() { + return m_kPostfix; + } + + /** + * Return the Volume RGBT. + * @return Volume RGBT. + */ + public ModelStorageBase getLUT() { + return (m_kImage != null ) ? m_kImage.isColorImage() ? m_kRGBT : m_kLUT : null; + } + + /** + * Return the Volume RGBT. + * @return Volume RGBT. + */ + public ModelRGB GetRGB() { + return m_kRGBT; + } + + public float GetTransferedValue( int x, int y, int z ) + { + int dimX = m_kImage.getExtents().length > 0 ? m_kImage.getExtents()[0] : 1; + int dimY = m_kImage.getExtents().length > 1 ? m_kImage.getExtents()[1] : 1; + int dimZ = m_kImage.getExtents().length > 2 ? m_kImage.getExtents()[2] : 1; + if ( x < 0 || x >= dimX || y < 0 || y >= dimY || z < 0 || z >= dimZ ) return -1; + + if ( m_kImage.isColorImage() ) { + float r = m_kRGBT.getROn() ? TransferValue(m_kImage.getFloat(x, y, z, 1)) : -1; + float g = m_kRGBT.getGOn() ? TransferValue(m_kImage.getFloat(x, y, z, 2)) : -1; + float b = m_kRGBT.getBOn() ? TransferValue(m_kImage.getFloat(x, y, z, 3)) : -1; + return Math.max( r, Math.max(g, b)); + } + float value = m_kImage.getFloat(x, y, z); + return TransferValue(value); + } + + private float TransferValue(float value) { + float min = (float) m_kImage.getMin(); + float max = (float) m_kImage.getMax(); + float diff = max - min; + byte index = 0; + if ( (diff > 1) && (diff <= 255) ) + { + index = (byte)(((value - min)/diff) * diff); + } + else + { + index = (byte)(((value - min)/diff) * 255); + } + if ( (index >= 0) && (index < 255) && (m_kColorMap != null) && (m_kColorMap.GetData() != null) ) + { + byte r = m_kColorMap.GetData()[index * 4 + 0]; + byte g = m_kColorMap.GetData()[index * 4 + 1]; + byte b = m_kColorMap.GetData()[index * 4 + 2]; + byte a = m_kColorMap.GetData()[index * 4 + 3]; + return Math.max(r*a, Math.max(g*a, b*a)); + } + return -1; + } + + + /** + * The ModelImage Volume max-scale factor. + * @return Volume max-scale factor. + */ + public float GetScaleMax() { + return m_fMax; + } + + public int GetMaxExtent() + { + return m_iMaxExtent; + } + + /** + * The ModelImage Volume x-scale factor. + * @return Volume x-scale factor. + */ + public float GetScaleX() { + return m_fX; + } + + /** + * The ModelImage Volume y-scale factor. + * @return Volume y-scale factor. + */ + public float GetScaleY() { + return m_fY; + } + + /** + * The ModelImage Volume z-scale factor. + * @return Volume z-scale factor. + */ + public float GetScaleZ() { + return m_fZ; + } + + /** + * Return the surface mask Texture. + * @return surface mask Texture. + */ + public Texture GetSurfaceTarget() { + return m_kSurfaceTarget; + } + + + + /** A vector of BitSet masks, one for each surface loaded into the viewer. */ + protected Vector surfaceMask; + /** A vector of the mask names, so they can be accessed by name: */ + protected Vector surfaceNames; + /** A vector of BitSet masks, one for each surface loaded into the viewer. */ + protected Vector surfaceColor; + /** + * Add a new surface mask. + * @param name surface name. + * @param mask surface mask volume. + */ + public void setSurfaceMask(String name, ColorRGB color, BitSet mask) + { + if ( surfaceMask == null ) + { + surfaceMask = new Vector(); + surfaceNames = new Vector(); + surfaceColor = new Vector(); + } + surfaceMask.add(mask); + surfaceNames.add(name); + surfaceColor.add(color); + updateMask(); + } + + private void updateMask() + { + boolean bUpdate = false; + final int iXBound = m_kImage.getExtents()[0]; + final int iYBound = m_kImage.getExtents()[1]; + final int iZBound = m_kImage.getExtents()[2]; + int length = iXBound * iYBound * iZBound; + for ( int i = 0; i < length; i++ ) + { + boolean color = false; + for ( int surface = 0; surface < surfaceMask.size(); surface++ ) + { + if ( surfaceMask.elementAt(surface).get(i) ) + { + m_kSurfaceImage.GetData()[i * 4 + 0] = (byte) (surfaceColor.elementAt(surface).R * 255); + m_kSurfaceImage.GetData()[i * 4 + 1] = (byte) (surfaceColor.elementAt(surface).G * 255); + m_kSurfaceImage.GetData()[i * 4 + 2] = (byte) (surfaceColor.elementAt(surface).B * 255); + m_kSurfaceImage.GetData()[i * 4 + 3] = (byte) (255); + bUpdate = true; + color = true; + } + } + if ( !color ) + { + m_kSurfaceImage.GetData()[i * 4 + 0] = (byte) (0); + m_kSurfaceImage.GetData()[i * 4 + 1] = (byte) (0); + m_kSurfaceImage.GetData()[i * 4 + 2] = (byte) (0); + m_kSurfaceImage.GetData()[i * 4 + 3] = (byte) (0); + } + } + if ( bUpdate ) + { + m_kSurfaceTarget.Reload(true); + } + } + + /** + * Delete the surface mask, using the name of the mask as reference. + * @param name the surface name. + */ + public void removeSurfaceMask(String name) + { + boolean bUpdate = false; + if ( surfaceMask != null && surfaceNames != null) + { + if ( surfaceNames.contains(name) ) + { + surfaceMask.remove( surfaceNames.indexOf(name) ); + surfaceColor.remove( surfaceNames.indexOf(name) ); + surfaceNames.remove(name); + bUpdate = true; + } + } + if ( bUpdate ) + { + updateMask(); + m_kSurfaceTarget.Reload(true); + } + } + + /** + * Delete the surface mask, using the name of the mask as reference. + * @param name the surface name. + */ + public void setSurfaceMaskColor(String name, ColorRGB color) + { + boolean bUpdate = false; + if ( surfaceMask != null && surfaceNames != null) + { + if ( surfaceNames.contains(name) ) + { + surfaceColor.elementAt( surfaceNames.indexOf(name) ).Copy(color); + bUpdate = true; + } + } + if ( bUpdate ) + { + updateMask(); + } + } + + + /** + * Returns the current rendered time-slice for 4D images. Otherwise returns 0. + * @return the current rendered time-slice for 4D images. Otherwise returns 0. + */ + public int GetTimeSlice() { + return m_iTimeSlice; + } + + /** + * Return the Texture containing the volume data. + * @return Texture containing the volume data. + */ + public Texture GetVolumeTarget() { + return m_kVolumeTarget; + } + + /** + * Return the Buffer containing the volume data, which is stored in the Texture GrapicsImage. + * @return Buffer containing the volume data. + */ + public Buffer GetVolumeTargetBuffer() { + return m_kVolumeTarget.GetImage().GetDataBuffer(); + } + + /** + * Initialize the GraphicsImage for the opacity lookup table. + * + * @param kImage the ModelImage the opacity transfer function applies to. + * @param kPostfix the string postfix to concatenate to the "OpacityMap" image name. + * @return GraphicsImage, the new GraphicsImage storing opacity lookup table. + */ + public GraphicsImage InitOpacityMap(final ModelImage kImage, final String kPostFix) { + final int iLutHeight = 256; + final float[] afData = new float[iLutHeight]; + final float fRange = (float) (kImage.getMax() - kImage.getMin()); + final float fStep = fRange / iLutHeight; + float fDataValue = (float) kImage.getMin(); + for (int i = 0; i < iLutHeight; i++) { + afData[i] = (float) (iLutHeight * (kImage.getMax() - fDataValue) / fRange); + fDataValue += fStep; + } + + return new GraphicsImage(GraphicsImage.FormatMode.IT_L8, iLutHeight, afData, + new String("OpacityMap" + kPostFix)); + } + + /** + * Return true if the Volume image is a color image. + * + * @return true if the Volume image is a color image. + */ + public boolean IsColorImage() { + return m_kImage.isColorImage(); + } + + /** + * Release the Textures containing the volume data. Once Textures are released, they will be re-loaded onto the GPU + * during the next frame. + */ + public void ReleaseVolume() { + m_kVolumeTarget.Reload(true); + } + + + + /** + * Called when the user selects the Gradient Magnitude option or the Multi-Histogram option + * in the Volume Renderer. + * @param kGradientMagnitude pre-computed GradientMagnitude image or null + * @param bComputeLaplace when true the Laplace image and multi-histogram histogram Textures are computed. + * @param kPostfix GraphicsImage postfix string. + */ + public void SetGradientMagnitude(ModelImage kGradientMagnitude, boolean bComputeLaplace, String kPostfix ) + { + int start = 0; + int end = m_kImage.getNDims() > 3 ? m_iTimeSteps : 1; + if ( !m_bGMInit ) + { + try { + // System.err.println( "SetGradientMagnitude " + 0 ); + m_kImageGM = getGradientMagnitude( m_kImage, 0 ); + if ( m_kImageGM != null ) + { + m_kVolumeGM[0] = createGM_Laplace(m_kImageGM, null, m_kVolumeGM[0], 0, true); + if ( m_kVolumeGM[0] != null ) + { + m_akGradientMagMinMax[0] = new Vector2f( (float)m_kImageGM.getMin(), (float)m_kImageGM.getMax() ); + m_kVolumeGMTarget.SetImage(m_kVolumeGM[0]); + m_kVolumeGMTarget.Reload(true); + m_bGMInit = true; + } + } + for (int i = 1; i < m_iTimeSteps; i++) + { + // System.err.println( "SetGradientMagnitude " + i ); + ModelImage gmImage = getGradientMagnitude( m_kImage, i ); + if ( gmImage != null ) + { + m_kVolumeGM[i] = createGM_Laplace(gmImage, null, m_kVolumeGM[i], i, true); + if ( m_kVolumeGM[i] != null ) + { + m_akGradientMagMinMax[i] = new Vector2f( (float)gmImage.getMin(), (float)gmImage.getMax() ); + gmImage.disposeLocal(); + end = i + 1; + } + } + } + } catch ( java.lang.OutOfMemoryError e ) {} + } + + if ( m_bGMInit && bComputeLaplace && (m_kImageGM != null)) + { + GenerateHistogram(m_kVolume, m_kVolumeGM, kPostfix, start, end ); + } + } + + /** + * Sets the ModelRGB for the iImage. + * + * @param kRGBT new ModelRGB + */ + public void SetRGBT(final ModelRGB kRGBT) { + if (kRGBT == null) { + return; + } + ModelLUT.exportIndexedLUTMin(kRGBT, m_kColorMap.GetData()); + m_kColorMapTarget.Reload(true); + m_kRGBT = kRGBT; + } + + /** + * Sets the time slices for 4D data. + * @param iSlice new time slice value. + */ + public void SetTimeSlice(final int iSlice) { + if (m_iTimeSlice != iSlice) { + m_iTimeSlice = iSlice; + update4D(); + } + } + + /** + * Updates the current time slice. + * @param bForward when true the time advances on step forward or wraps to the beginning. + * When false the time moves backward. + */ + public void update4D(final boolean bForward) { + if (m_iTimeSteps == 1) { + return; + } + if (bForward) { + m_iTimeSlice++; + } else { + m_iTimeSlice--; + } + if (m_iTimeSlice >= m_iTimeSteps) { + m_iTimeSlice = 0; + } + if (m_iTimeSlice < 0) { + m_iTimeSlice = m_iTimeSteps - 1; + } + + update4D(); + } + + /** + * Update the image data. + * + * @param kImage the modified ModelImage + * @param bCopytoCPU when true the data is copied from the GPU GraphicsImage into the ModelImage + */ + public void UpdateData(final ModelImage kImage, boolean reload) { + m_kImage = kImage; + if ( m_kVolume == null ) { + m_kPostfix = ""; + init(null, 0, false); + return; + } + initLUT(); + if ( reload ) + { + if ( m_kVolume[m_iTimeSlice] != null ) + { + m_kVolume[m_iTimeSlice].dispose(); + } + m_kVolumeTarget.Remove(); + m_kVolume[m_iTimeSlice] = initVolumeData(m_kImage, m_iTimeSlice, m_kVolumeTarget, m_kImage.getImageName(), true, false); + m_kVolumeTarget.SetImage(m_kVolume[m_iTimeSlice]); + } + else + { + m_kVolume[m_iTimeSlice] = resetVolumeData(m_kImage, m_iTimeSlice, m_kVolume[m_iTimeSlice], m_kVolumeTarget, m_kImage + .getImageName(), true, false); + } + InitScale(); + } + + + /** + * Changes the underlying image data and LUT. If the new image data is a different size than + * then previous one, recreate the volume image on the GPU, otherwise just overwrite it with + * the new data. + * @param kImage + * @param kLUT + * @param reload + */ + public void UpdateData(final ModelImage kImage, ModelLUT kLUT, boolean reload) { + if ( kLUT == null && !kImage.isColorImage()) + { + UpdateData(kImage, reload); + return; + } + m_kImage = kImage; + m_kLUT = kLUT; + if ( reload ) + { + if ( m_kVolume[m_iTimeSlice] != null ) + { + m_kVolume[m_iTimeSlice].dispose(); + } + m_kVolumeTarget.Remove(); + m_kVolume[m_iTimeSlice] = initVolumeData(m_kImage, m_iTimeSlice, m_kVolumeTarget, m_kImage.getImageName(), true, false); + m_kVolumeTarget.SetImage(m_kVolume[m_iTimeSlice]); + } + else + { + m_kVolume[m_iTimeSlice] = resetVolumeData(m_kImage, m_iTimeSlice, m_kVolume[m_iTimeSlice], m_kVolumeTarget, m_kImage + .getImageName(), true, false); + } + InitScale(); + } + + /** + * Update the LUT for the ModelImage. + * + * @param kLUT new LUT for ModelImage. + */ + public void UpdateImages(final ModelLUT kLUT) { + if (kLUT != null) { + VolumeImage.UpdateImages(m_kColorMapTarget, m_kColorMap, kLUT); + m_kLUT = kLUT; + } + } + + /** + * Update the LUT for the ModelImage. + * + * @param kLUT new LUT for ModelImage. + */ + public void UpdateImages(final ModelStorageBase kLUT) { + if ( (kLUT != null) && (kLUT instanceof ModelLUT)) { + VolumeImage.UpdateImages(m_kColorMapTarget, m_kColorMap, (ModelLUT)kLUT); + m_kLUT = (ModelLUT)kLUT; +// System.err.println("UpdateImages " + m_kColorMapTarget.GetName() + " " + kLUT. +// System.err.println(" " + m_kColorMapTarget.GetID() ); + + } + if ( (kLUT != null) && (kLUT instanceof ModelRGB)) { + ModelLUT.exportIndexedLUTMin((ModelRGB)kLUT, m_kColorMap.GetData()); + m_kColorMapTarget.Reload(true); + m_kRGBT = (ModelRGB)kLUT; + } + } + + /** + * Update the transfer function for the image iImage. + * + * @param kTransfer the new opacity transfer function + * @param iImage the image to modify (0 = volume image, 2 = gradient mag) + * @param kImage GradientMagitude image. + * @return boolean true when updated, false otherwise. + */ + public boolean UpdateImages(final TransferFunction kTransfer, final int iImage, final ModelImage kImage) { + if (iImage == 0) { + return UpdateImages2(m_kImage, m_kColorMapTarget, m_kColorMap, kTransfer); + } else if ( (iImage == 2) && (kImage != null) && (m_kOpacityMapTarget_GM != null) && (m_kOpacityMap_GM != null)) { + return UpdateImages(kImage, m_kOpacityMapTarget_GM, m_kOpacityMap_GM, kTransfer); + } + return false; + } + + /** + * In order to map line integrals of image intensity to RGB colors where each color channel is 8 bits, it is + * necessary to make sure that the integrals are in [0,255]. Producing a theoretical maximum value of a line + * integral is not tractable in an application. This method constructs an approximate maximum by integrating along + * each line of voxels in the image with line directions parallel to the coordinate axes. The 'processRay' call + * adjusts the line integrals using the estimate, but still clamps the integrals to 255 since the estimate might not + * be the true maximum. + * + * @return float Integral normalization factor. + */ + protected float computeIntegralNormalizationFactor() { + final int iXBound = m_kImage.getExtents()[0]; + final int iYBound = m_kImage.getExtents()[1]; + final int iZBound = m_kImage.getExtents()[2]; + + byte[] aucData = null; + int iSize = iXBound * iYBound * iZBound; + if (m_kImage.isColorImage()) { + iSize *= 4; + } + + aucData = new byte[iSize]; + + try { + m_kImage.exportDataUseMask(0, iSize, false, aucData); + } catch (final IOException e) { + e.printStackTrace(); + } + + // compute image normalization factor + int iX, iY, iZ, iBase, iSteps; + float fMaxIntegral = 0.0f; + float fTStep, fIntegral; + + // fix y and z, integrate over x + for (iY = 0; iY < iYBound; iY++) { + + for (iZ = 0; iZ < iZBound; iZ++) { + iBase = iXBound * (iY + (iYBound * iZ)); + iSteps = iXBound - 1; + fIntegral = 0.5f * ( (aucData[iBase] & 0x0ff) + (aucData[iBase + iSteps] & 0x0ff)); + fTStep = 1.0f / iSteps; + + for (iX = 1; iX < iSteps; iX++) { + fIntegral += (aucData[iBase + iX] & 0x0ff); + } + + fIntegral *= fTStep; + + if (fIntegral > fMaxIntegral) { + fMaxIntegral = fIntegral; + } + } + } + final int iXYProduct = iXBound * iYBound; + // fix x and z, integrate over y + for (iX = 0; iX < iXBound; iX++) { + + for (iZ = 0; iZ < iZBound; iZ++) { + iBase = iX + (iXYProduct * iZ); + iSteps = iYBound - 1; + fIntegral = 0.5f * ( (aucData[iBase] & 0x0ff) + (aucData[iBase + (iXBound * iSteps)] & 0x0ff)); + fTStep = 1.0f / iSteps; + + for (iY = 1; iY < iSteps; iY++) { + fIntegral += (aucData[iBase + (iXBound * iY)] & 0x0ff); + } + + fIntegral *= fTStep; + + if (fIntegral > fMaxIntegral) { + fMaxIntegral = fIntegral; + } + } + } + + // fix x and y, integrate over z + for (iX = 0; iX < iXBound; iX++) { + + for (iY = 0; iY < iYBound; iY++) { + iBase = iX + (iXBound * iY); + iSteps = iZBound - 1; + fIntegral = 0.5f * ( (aucData[iBase] & 0x0ff) + (aucData[iBase + (iXYProduct * iSteps)] & 0x0ff)); + fTStep = 1.0f / iSteps; + + for (iZ = 1; iZ < iSteps; iZ++) { + fIntegral += (aucData[iBase + (iXYProduct * iZ)] & 0x0ff); + } + + fIntegral *= fTStep; + + if (fIntegral > fMaxIntegral) { + fMaxIntegral = fIntegral; + } + } + } + aucData = null; + return (fMaxIntegral > 0.0f) ? (1.0f / fMaxIntegral) : 0.00f; + } + + /** + * Checks that the two input images match extents, units of measure and resolutions. The images + * may had different sizes (3D or 4D) the first 3-dimensions must match. + * @param kImage1 + * @param kImage2 + * @return true if the images match extends, units and resolutions. + */ + public static boolean checkImage(ModelImage kImage1, ModelImage kImage2 ) + { + for ( int i = 0; i < Math.min( kImage1.getExtents().length, kImage2.getExtents().length ); i++ ) + { + if ( kImage1.getExtents()[i] != kImage2.getExtents()[i] ) + { + return false; + } + if ( kImage1.getUnitsOfMeasure()[i] != kImage2.getUnitsOfMeasure()[i] ) + { + return false; + } + if ( kImage1.getResolutions(0)[i] != kImage2.getResolutions(0)[i] ) + { + return false; + } + } + return true; + } + + /** + * Generate 2D histogram from the input image and the gradient-magnitude + * + * @param kImage input GraphicsImage containing the ModelImage data + * @param kImageGM input GraphcisImage containing the Gradient Magnitude data. + * @param kPostFix post-fix for the image name. + */ + private void GenerateHistogram(final GraphicsImage[] kImage, final GraphicsImage[] kImageGM, final String kPostFix, + int start, int end ) + { + int iTMinX = 255, iTMaxX = 0; + int iTMinY = 255, iTMaxY = 0; + float max = Float.MIN_VALUE; + float min = Float.MAX_VALUE; + m_kHisto = new GraphicsImage[m_iTimeSteps]; + for (int t = start; t < end; t++) { + float[] afCount = new float[256 * 256]; + for (int i = 0; i < 256 * 256; i++) { + afCount[i] = 0; + } + + int a1; + int a2; + final byte[] abHistoData = kImageGM[t].GetData(); + final byte[] abData = kImage[t].GetData(); + if (m_kImage.isColorImage()) { + int iHisto = 0; + for (int i = 0; i < abData.length; i += 4) { + int iR = (abData[i]); + int iG = (abData[i + 1]); + int iB = (abData[i + 2]); + //a1 = (iR * 0.299 + iG * 0.587 + iB * 0.114); + a1 = (iR + iG + iB)/3; + a1 = (a1 & 0x00ff); + + iR = (abHistoData[i]); + iG = (abHistoData[i + 1]); + iB = (abHistoData[i + 2]); + //a2 = (short) (iR * 0.299 + iG * 0.587 + iB * 0.114); + a2 = (iR + iG + iB)/3; + a2 = (a2 & 0x00ff); + afCount[a1 + a2 * 256] += 1; + iHisto++; + } + } + else { + int iHisto = 0; + for (int i = 0; i < abData.length; i += 4) { + a1 = abData[i]; + a1 = (a1 & 0x00ff); + a2 = (abHistoData[iHisto]); + a2 = (a2 & 0x00ff); + afCount[a1 + a2 * 256] += 1; + iHisto += 4; + } + } + max = Float.MIN_VALUE; + min = Float.MAX_VALUE; + for (int i = 0; i < 256 * 256; ++i) { + afCount[i] = (float) Math.log(afCount[i]+1); + max = Math.max(afCount[i], max); + min = Math.min(afCount[i], min); + } + //System.err.println( min + " " + max ); + final byte[] abHisto = new byte[256 * 256]; + //int maxB = Integer.MIN_VALUE; + //int minB = Integer.MAX_VALUE; + for (int i = 0; i < 256 * 256; ++i) { + abHisto[i] = new Float((afCount[i] / max) * 255f).byteValue(); + //maxB = ( iVal > maxB ) ? iVal : maxB; + //minB = ( iVal < minB ) ? iVal : minB; + } + afCount = null; + + int iMinX = 255, iMaxX = 0; + int iIndex = 0; + for (int i = 0; i < 256; i++) { + for (int j = 0; j < 256; j++) { + iIndex = i * 256 + j; + if (abHisto[iIndex] > 50) { + if (iMinX > j) { + iMinX = j; + } + if (j > iMaxX) { + iMaxX = j; + } + } + } + } + + int iMinY = 255, iMaxY = 0; + for (int j = 0; j < 256; j++) { + for (int i = 0; i < 256; i++) { + iIndex = i * 256 + j; + if (abHisto[iIndex] > 50) { + if (iMinY > i) { + iMinY = i; + } + if (i > iMaxY) { + iMaxY = i; + } + } + } + } + if (iTMinX > iMinX) { + iTMinX = iMinX; + } + if (iTMaxX < iMaxX) { + iTMaxX = iMaxX; + } + + if (iTMinY > iMinY) { + iTMinY = iMinY; + } + if (iTMaxY < iMaxY) { + iTMaxY = iMaxY; + } + + // iMinX = 0; iMaxX = 255; + // iMinY = 0; iMaxY = 255; + + m_kHisto[t] = new GraphicsImage(GraphicsImage.FormatMode.IT_L8, 256, 256, (byte[]) null, new String( + "VolumeImageHisto" + kPostFix)); + m_kHisto[t].SetData(abHisto, 256, 256); + /* + ModelImage kTestHisto2D = new ModelImage( ModelStorageBase.UBYTE, new int[]{256,256}, "Histo2D" ); + try { + kTestHisto2D.importData(abHisto); + } catch (IOException e) { + e.printStackTrace(); + } + kTestHisto2D.calcMinMax(); + new ViewJFrameImage( kTestHisto2D ); */ + } + + m_kHistoTarget = new Texture(); + m_kHistoTarget.SetImage(m_kHisto[0]); + m_kHistoTarget.SetShared(true); + m_kHistoTarget.SetFilterType(Texture.FilterType.LINEAR); + m_kHistoTarget.SetWrapType(0, Texture.WrapType.CLAMP_BORDER); + m_kHistoTarget.SetWrapType(1, Texture.WrapType.CLAMP_BORDER); + m_kHistoTarget.SetWrapType(2, Texture.WrapType.CLAMP_BORDER); + + iTMinX = 0; + iTMaxX = Math.max( iTMaxX, iTMaxY ); + m_akHistoTCoord = new Vector2f[4]; + m_akHistoTCoord[0] = new Vector2f(iTMinX / 255.0f, iTMinX / 255.0f); + m_akHistoTCoord[1] = new Vector2f(iTMaxX / 255.0f, iTMinX / 255.0f); + m_akHistoTCoord[2] = new Vector2f(iTMaxX / 255.0f, iTMaxX / 255.0f); + m_akHistoTCoord[3] = new Vector2f(iTMinX / 255.0f, iTMaxX / 255.0f); + //m_akHistoTCoord[0] = new Vector2f(0f, 0f); + //m_akHistoTCoord[1] = new Vector2f(1f, 0f); + //m_akHistoTCoord[2] = new Vector2f(1f, 1f); + //m_akHistoTCoord[3] = new Vector2f(0f, 1f); + m_bHistoInit = true; + } + + /** + * Calculates and stores the gradient magnitude images (3D or 4D) for the input image. Or reads from disk. + * The data is stored in the GraphicsImage data structures and will be passed to the GPU to use in rendering. + * + * @param kImage input image + * @param kGradientMagnitude input Gradient Magnitude image, or null. + * @param bComputeLaplace when true the Laplace image is also calculated or read from dis. + private void GradientMagnitudeImage(final ModelImage kImage, ModelImage kGradientMagnitude, + boolean bComputeLaplace) { + + + if ( !m_bGMInit ) + { + ModelImage[] kImageGM = new ModelImage[m_iTimeSteps]; + String[] kImageName = new String[m_iTimeSteps]; + for (int i = 0; i < m_iTimeSteps; i++) { + kImageName[i] = ModelImage.makeImageName(kImage.getFileInfo(0).getFileName(), new String("_GM_" + i)); + + if ( kGradientMagnitude != null && checkImage(kImage, kGradientMagnitude )) + { + m_kVolumeGM[i] = VolumeImage.UpdateData(kGradientMagnitude, i, null, m_kVolumeGM[i], + m_kVolumeGMTarget, m_kVolumeGM[i].GetName(), true, false); + ModelImage.saveImage( kGradientMagnitude, kImageName[i] + ".xml", m_kDir ); + } + else + { + kImageGM[i] = ReadFromDisk(kImageName[i] + ".xml", m_kDir); + if ( kImageGM[i] != null && !checkImage(kImage, kImageGM[i] ) ) + { + kImageGM[i].disposeLocal(); + kImageGM[i] = null; + } + if (kImageGM[i] == null) { + JDialogGradientMagnitude kCalcMagnitude = new JDialogGradientMagnitude(null, m_akImages[i]); + kCalcMagnitude.setVisible(false); + kCalcMagnitude.setOutputNewImage(true); + kCalcMagnitude.setDisplayProgressBar(true); + kCalcMagnitude.setSeparateThread(false); + kCalcMagnitude.setSeparable(true); + kCalcMagnitude.setUseOCL(true); + kCalcMagnitude.actionPerformed(new ActionEvent(this, 0, "OK")); + kImageGM[i] = kCalcMagnitude.getResultImage(); + kCalcMagnitude = null; + } + if (kImageGM[i] == null) { + System.err.println("Gradient magnitude calculation returned null"); + m_kVolumeGM[i] = VolumeImage.UpdateData(kImage, i, null, m_kVolumeGM[i], m_kVolumeGMTarget, kImageName[i], true, false); + } else { + kImageGM[i].calcMinMax(); + m_akGradientMagMinMax[i] = new Vector2f( (float)kImageGM[i].getMin(), (float)kImageGM[i].getMax() ); + + if ( !( bComputeLaplace && !m_kImage.isColorImage() ) ) + { + m_kVolumeGM[i] = VolumeImage.UpdateData(kImageGM[i], 0, null, m_kVolumeGM[i], m_kVolumeGMTarget, kImageName[i], true, false); + } + } + } + } + + + if ( bComputeLaplace && !m_kImage.isColorImage() ) + { + for (int i = 0; i < m_iTimeSteps; i++) { + final String kImageNameL = ModelImage.makeImageName(kImage.getFileInfo(0).getFileName(), new String( + "_Laplacian_" + i)); + ModelImage kImageGMGM = null; + kImageGMGM = ReadFromDisk(kImageNameL + ".xml", m_kDir); + if ( kImageGMGM != null && !checkImage(kImage, kImageGMGM ) ) + { + kImageGMGM.disposeLocal(); + kImageGMGM = null; + } + if (kImageGMGM == null) { + final JDialogLaplacian kCalcLaplacian = new JDialogLaplacian(null, m_akImages[i]); + kCalcLaplacian.setVisible(false); + kCalcLaplacian.setOutputNewImage(true); + kCalcLaplacian.setDisplayProgressBar(true); + kCalcLaplacian.setSeparateThread(false); + kCalcLaplacian.setUseOCL(true); + kCalcLaplacian.setSeparable(true); + kCalcLaplacian.actionPerformed(new ActionEvent(this, 0, "OK")); + kImageGMGM = kCalcLaplacian.getResultImage(); + } + if (kImageGMGM != null) { + m_kVolumeGM[i] = createGM_Laplace(kImageGM[i], kImageGMGM, m_kVolumeGM[i], i, true); + } else { + m_kVolumeGM[i] = VolumeImage.UpdateData(kImageGM[i], 0, null, m_kVolumeGM[i], m_kVolumeGMTarget, kImageName[i], true, false); + } + final ViewJFrameImage kImageFrame = ViewUserInterface.getReference().getFrameContainingImage(kImageGMGM); + if (kImageFrame != null) { + kImageFrame.close(); + } else if (kImageGMGM != null) { + kImageGMGM.disposeLocal(); + kImageGMGM = null; + } + } + } + + m_bGMInit = true; + m_kVolumeGMTarget.SetImage(m_kVolumeGM[0]); + m_kVolumeGMTarget.Reload(true); + + for ( int i = 0; i < kImageGM.length; i++ ) + { + if (kImageGM[i] != null) { + + kImageGM[i].setImageDirectory( m_kDir ); + kImageGM[i].setImageName( kImageName[i] + ".xml" ); + ModelImage.saveImage(kImageGM[i], kImageName[i] + ".xml", m_kDir ); + + + final ViewJFrameImage kImageFrame = ViewUserInterface.getReference().getFrameContainingImage(kImageGM[i]); + if (kImageFrame != null) { + kImageFrame.close(); + } + + kImageGM[i].disposeLocal(); + kImageGM[i] = null; + } + } + } + } + */ + + public static ModelImage getGradientMagnitude( ModelImage kImage, int i ) + { + if ( kImage == null ) + { + return null; + } + int index = kImage.getExtents()[2] / 2; + float xRes = kImage.getFileInfo(index).getResolutions()[0]; + float zRes = kImage.getFileInfo(index).getResolutions()[2]; + + float correction = xRes / zRes; + float[] sigmas = new float[]{1f,1f,correction}; + + int dimX = kImage.getExtents().length > 0 ? kImage.getExtents()[0] : 1; + int dimY = kImage.getExtents().length > 1 ? kImage.getExtents()[1] : 1; + int dimZ = kImage.getExtents().length > 2 ? kImage.getExtents()[2] : 1; + ModelImage outputImage = new ModelImage( kImage.getDataType(), new int[]{dimX,dimY,dimZ}, "temp" ); + AlgorithmGradientMagnitudeSep gradientMagAlgo = new AlgorithmGradientMagnitudeSep( kImage, sigmas, true, false ); +// OpenCLAlgorithmGradientMagnitude gradientMagAlgo = new OpenCLAlgorithmGradientMagnitude(outputImage, kImage, sigmas, +// true, true, false); + gradientMagAlgo.setRed(true); + gradientMagAlgo.setGreen(true); + gradientMagAlgo.setBlue(true); + gradientMagAlgo.setRunningInSeparateThread(false); + gradientMagAlgo.run(); + float[] resultBuffer = gradientMagAlgo.getResultBuffer(); + try { + outputImage.importData(0, resultBuffer, true); + } catch (IOException e) {} +// gradientMagAlgo.gradientMagnitudeSep3D( i ); + gradientMagAlgo.finalize(); + gradientMagAlgo = null; + return outputImage; + } + + +// private ModelImage getLaplace( ModelImage kImage, int i ) +// { +// String kImageName = ModelImage.makeImageName(kImage.getFileInfo(0).getFileName(), new String("_Laplacian_" + i)); +// ModelImage kImageL = ReadFromDisk(kImageName + ".xml", m_kDir); +// if ( kImageL != null && !checkImage(kImage, kImageL ) ) +// { +// kImageL.disposeLocal(); +// kImageL = null; +// } +// if (kImageL == null) { +// JDialogLaplacian kCalcLaplacian = new JDialogLaplacian(null, m_akImages[i]); +// kCalcLaplacian.setVisible(false); +// kCalcLaplacian.setOutputNewImage(true); +// kCalcLaplacian.setDisplayProgressBar(true); +// kCalcLaplacian.setSeparateThread(false); +// kCalcLaplacian.setUseOCL(true); +// kCalcLaplacian.setSeparable(true); +// kCalcLaplacian.actionPerformed(new ActionEvent(this, 0, "OK")); +// kImageL = kCalcLaplacian.getResultImage(); +// kCalcLaplacian = null; +// +// kImageL.setImageDirectory( m_kDir ); +// kImageL.setImageName( kImageName + ".xml" ); +// JDialogBase.updateFileInfo( kImage, kImageL ); +// ModelImage.saveImage(kImageL, kImageName + ".xml", m_kDir ); +// final ViewJFrameImage kImageFrame = ViewUserInterface.getReference().getFrameContainingImage(kImageL); +// if (kImageFrame != null) { +// kImageFrame.setVisible(false); +// } +// } +// return kImageL; +// } + + /** + * Initialize the VolumeImage with the ModelImage data. + * @param kProgress progress bar + * @param iProgress progress bar increment + */ + private void init(final ViewJProgressBar kProgress, final int iProgress, boolean initGradientMagnitude) { + // Create LUTS for the ModelImage: + initLUT(); + // Initialize Texture Maps: + if ( !m_kImage.isColorImage() ) + { + initImages(); + } + else + { + initImagesColor(); + } + if ( initGradientMagnitude ) + { + SetGradientMagnitude(null, true, m_kPostfix); + } + if (kProgress != null) { + kProgress.updateValueImmed(kProgress.getValue() + iProgress); + } + } + + /** + * Intializes the Textures and GraphicsImages used to render the ModelImage this + * VolumeImage represents. + */ + private void initImages() { + m_fDRRNormalize = computeIntegralNormalizationFactor(); + // Initialize Color Map GraphicsImage: + m_kColorMap = initColorMap(); + // Initialize Opacity Map for the GradientMagnitude image: + m_kOpacityMap_GM = InitOpacityMap(m_kImage, new String(m_kPostfix + "_GM")); + + final int iXBound = m_kImage.getExtents()[0]; + final int iYBound = m_kImage.getExtents()[1]; + final int iZBound = m_kImage.getExtents()[2]; + + /* + * Map the ModelImage volume data to a texture image, including for the ModelImage gradient magnitude data: + */ + final int[] aiExtents = m_kImage.getExtents(); + final int iNDims = aiExtents.length; + String kImageName; + GraphicsImage.FormatMode type = GraphicsImage.FormatMode.IT_RGBA8888 ; + + + if (iNDims == 3) { // ModelImage is 3D: + m_iTimeSteps = 1; + } + else { // ModelImage is 4D: + m_iTimeSteps = aiExtents[3]; + } + // Allocate a 3D GraphicsImage for each 3D Volume + m_kVolume = new GraphicsImage[m_iTimeSteps]; + m_kVolumeGM = new GraphicsImage[m_iTimeSteps]; + m_akGradientMagMinMax = new Vector2f[m_iTimeSteps]; + + final int[] aiSubset = new int[] {aiExtents[0], aiExtents[1], aiExtents[2]}; + + for (int i = 0; i < m_iTimeSteps; i++) { +// System.err.println( "initiImages : " + i ); + if ( m_iTimeSteps > 1 ) + { + // Will generate the ModelImage GraphicsImage representation and separate the 4D ModelImage into + // the 3D Subset image. + m_kVolume[i] = initVolumeData(m_kImage, i, m_kVolumeTarget, m_kImage.getImageName(), true, false); + } + else + { + // Already 3D, just generate the GraphicsImage: + m_kVolume[0] = initVolumeData(m_kImage, m_iTimeSlice, m_kVolumeTarget, m_kImage.getImageName(), true, false); + } + + // Allocate GraphcisImage for Gradient Magnitude Texture: + kImageName = ModelImage.makeImageName(m_kImage.getFileInfo(0).getFileName(), + new String("_GM_" + i)); + m_kVolumeGM[i] = new GraphicsImage(type, iXBound, iYBound, iZBound, + (byte[])null, kImageName); + + } + // Initialize the Gradient Magnitude Texture and set its GraphicsImage: + m_kVolumeGMTarget = new Texture(); + m_kVolumeGMTarget.SetImage(m_kVolumeGM[0]); + m_kVolumeGMTarget.SetShared(true); + m_kVolumeGMTarget.SetFilterType(Texture.FilterType.LINEAR); + m_kVolumeGMTarget.SetWrapType(0, Texture.WrapType.CLAMP_BORDER); + m_kVolumeGMTarget.SetWrapType(1, Texture.WrapType.CLAMP_BORDER); + m_kVolumeGMTarget.SetWrapType(2, Texture.WrapType.CLAMP_BORDER); + + // Initialize the ModelImage Texture and set its GraphicsImage: + m_kVolumeTarget = new Texture(); + m_kVolumeTarget.SetImage(m_kVolume[0]); + m_kVolumeTarget.SetShared(true); + m_kVolumeTarget.SetFilterType(Texture.FilterType.LINEAR); + m_kVolumeTarget.SetWrapType(0, Texture.WrapType.CLAMP_BORDER); + m_kVolumeTarget.SetWrapType(1, Texture.WrapType.CLAMP_BORDER); + m_kVolumeTarget.SetWrapType(2, Texture.WrapType.CLAMP_BORDER); + + // Initialize the ColorMap Texture and set its GraphicsImage: + m_kColorMapTarget = new Texture(); + m_kColorMapTarget.SetImage(m_kColorMap); + m_kColorMapTarget.SetShared(true); + + // Initialize the Normal Map Texture and set its GraphicsImage: + m_kScratchTarget = new Texture(); + m_kScratchTarget.SetImage(new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, + (byte[])null, "ScratchBuffer")); + m_kScratchTarget.SetShared(true); + m_kScratchTarget.SetFilterType(Texture.FilterType.LINEAR); + m_kScratchTarget.SetWrapType(0, Texture.WrapType.CLAMP_BORDER); + m_kScratchTarget.SetWrapType(1, Texture.WrapType.CLAMP_BORDER); + m_kScratchTarget.SetWrapType(2, Texture.WrapType.CLAMP_BORDER); + + // Initialize the Opacity Map for the Gradient Magnitude Texture and set its GraphicsImage: + m_kOpacityMapTarget_GM = new Texture(); + m_kOpacityMapTarget_GM.SetImage(m_kOpacityMap_GM); + m_kOpacityMapTarget_GM.SetShared(true); + + // Initialize the Surface Mask Texture and set its GraphicsImage: + m_kSurfaceImage = new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, + new byte[4* iXBound * iYBound * iZBound], "SurfaceImage"); + m_kSurfaceTarget = new Texture(); + m_kSurfaceTarget.SetImage(m_kSurfaceImage); + m_kSurfaceTarget.SetShared(true); + m_kSurfaceTarget.SetFilterType(Texture.FilterType.LINEAR); + m_kSurfaceTarget.SetWrapType(0, Texture.WrapType.CLAMP_BORDER); + m_kSurfaceTarget.SetWrapType(1, Texture.WrapType.CLAMP_BORDER); + m_kSurfaceTarget.SetWrapType(2, Texture.WrapType.CLAMP_BORDER); + + // Calculate the scale factors for rendering the volume with a unit cube: + InitScale(); + } + + + private void initImagesColor() { + m_fDRRNormalize = computeIntegralNormalizationFactor(); + // Initialize Color Map GraphicsImage: + m_kColorMap = initColorMap(); + // Initialize Opacity Map for the GradientMagnitude image: + m_kOpacityMap_GM = InitOpacityMap(m_kImage, new String(m_kPostfix + "_GM")); + + final int iXBound = m_kImage.getExtents()[0]; + final int iYBound = m_kImage.getExtents()[1]; + final int iZBound = m_kImage.getExtents()[2]; + + /* + * Map the ModelImage volume data to a texture image, including for the ModelImage gradient magnitude data: + */ + final int[] aiExtents = m_kImage.getExtents(); + final int iNDims = aiExtents.length; + String kImageName; + GraphicsImage.FormatMode type = GraphicsImage.FormatMode.IT_RGBA8888 ; + + + if (iNDims == 3) { // ModelImage is 3D: + m_iTimeSteps = 1; + } + else { // ModelImage is 4D: + m_iTimeSteps = aiExtents[3]; + } + // A 4D ModelImage is separated into the 3D Volumes: +// m_akImages = new ModelImage[m_iTimeSteps]; + // Allocate a 3D GraphicsImage for each 3D Volume + m_kVolume = new GraphicsImage[m_iTimeSteps]; + m_kVolumeGM = new GraphicsImage[m_iTimeSteps]; + m_kNormal = new GraphicsImage[m_iTimeSteps]; + m_akGradientMagMinMax = new Vector2f[m_iTimeSteps]; + + final int[] aiSubset = new int[] {aiExtents[0], aiExtents[1], aiExtents[2]}; + for (int i = 0; i < m_iTimeSteps; i++) { + + if ( m_iTimeSteps > 1 ) + { + // Will generate the ModelImage GraphicsImage representation and separate the 4D ModelImage into + // the 3D Subset image. +// m_akImages[i] = new ModelImage(m_kImage.getType(), aiSubset, JDialogBase.makeImageName(m_kImage +// .getImageName(), "_" + i)); +// JDialogBase.updateFileInfo( m_kImage, m_akImages[i] ); + m_kVolume[i] = initVolumeData(m_kImage, i, m_kVolumeTarget, m_kImage.getImageName(), true, false); +// m_akImages[i].copyFileTypeInfo(m_kImage); +// m_akImages[i].calcMinMax(); + } + else + { + // Already 3D, just generate the GraphicsImage: +// m_akImages[0] = m_kImage; + m_kVolume[0] = initVolumeData(m_kImage, m_iTimeSlice, m_kVolumeTarget, m_kImage.getImageName(), true, false); + } + // Allocate GraphcisImage for Normal Map Texture: + kImageName = ModelImage.makeImageName(m_kImage.getFileInfo(0).getFileName(), + new String("_Normal_" + i)); + m_kNormal[i] = new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, + (byte[])null, kImageName); + + // Allocate GraphcisImage for Gradient Magnitude Texture: + kImageName = ModelImage.makeImageName(m_kImage.getFileInfo(0).getFileName(), + new String("_GM_" + i)); + m_kVolumeGM[i] = new GraphicsImage(type, iXBound, iYBound, iZBound, + (byte[])null, kImageName); + } + // Initialize the Gradient Magnitude Texture and set its GraphicsImage: + m_kVolumeGMTarget = new Texture(); + m_kVolumeGMTarget.SetImage(m_kVolumeGM[0]); + m_kVolumeGMTarget.SetShared(true); + m_kVolumeGMTarget.SetFilterType(Texture.FilterType.LINEAR); + m_kVolumeGMTarget.SetWrapType(0, Texture.WrapType.CLAMP_BORDER); + m_kVolumeGMTarget.SetWrapType(1, Texture.WrapType.CLAMP_BORDER); + m_kVolumeGMTarget.SetWrapType(2, Texture.WrapType.CLAMP_BORDER); + + // Initialize the ModelImage Texture and set its GraphicsImage: + m_kVolumeTarget = new Texture(); + m_kVolumeTarget.SetImage(m_kVolume[0]); + m_kVolumeTarget.SetShared(true); + m_kVolumeTarget.SetFilterType(Texture.FilterType.LINEAR); + m_kVolumeTarget.SetWrapType(0, Texture.WrapType.CLAMP_BORDER); + m_kVolumeTarget.SetWrapType(1, Texture.WrapType.CLAMP_BORDER); + m_kVolumeTarget.SetWrapType(2, Texture.WrapType.CLAMP_BORDER); + + // Initialize the ColorMap Texture and set its GraphicsImage: + m_kColorMapTarget = new Texture(); + m_kColorMapTarget.SetImage(m_kColorMap); + m_kColorMapTarget.SetShared(true); + + // Initialize the Normal Map Texture and set its GraphicsImage: + m_kNormalMapTarget = new Texture(); + m_kNormalMapTarget.SetImage(m_kNormal[0]); + m_kNormalMapTarget.SetShared(true); + m_kNormalMapTarget.SetFilterType(Texture.FilterType.LINEAR); + m_kNormalMapTarget.SetWrapType(0, Texture.WrapType.CLAMP_BORDER); + m_kNormalMapTarget.SetWrapType(1, Texture.WrapType.CLAMP_BORDER); + m_kNormalMapTarget.SetWrapType(2, Texture.WrapType.CLAMP_BORDER); + + + // Initialize the Normal Map Texture and set its GraphicsImage: + m_kScratchTarget = new Texture(); + m_kScratchTarget.SetImage(new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, + (byte[])null, "ScratchBuffer")); + m_kScratchTarget.SetShared(true); + m_kScratchTarget.SetFilterType(Texture.FilterType.LINEAR); + m_kScratchTarget.SetWrapType(0, Texture.WrapType.CLAMP_BORDER); + m_kScratchTarget.SetWrapType(1, Texture.WrapType.CLAMP_BORDER); + m_kScratchTarget.SetWrapType(2, Texture.WrapType.CLAMP_BORDER); + + // Initialize the Opacity Map for the Gradient Magnitude Texture and set its GraphicsImage: + m_kOpacityMapTarget_GM = new Texture(); + m_kOpacityMapTarget_GM.SetImage(m_kOpacityMap_GM); + m_kOpacityMapTarget_GM.SetShared(true); + + // Initialize the Surface Mask Texture and set its GraphicsImage: + m_kSurfaceImage = new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, iXBound, iYBound, iZBound, + new byte[4* iXBound * iYBound * iZBound], "SurfaceImage"); + m_kSurfaceTarget = new Texture(); + m_kSurfaceTarget.SetImage(m_kSurfaceImage); + m_kSurfaceTarget.SetShared(true); + m_kSurfaceTarget.SetFilterType(Texture.FilterType.LINEAR); + m_kSurfaceTarget.SetWrapType(0, Texture.WrapType.CLAMP_BORDER); + m_kSurfaceTarget.SetWrapType(1, Texture.WrapType.CLAMP_BORDER); + m_kSurfaceTarget.SetWrapType(2, Texture.WrapType.CLAMP_BORDER); + + // Calculate the scale factors for rendering the volume with a unit cube: + InitScale(); + } + + /** + * Create a new LUT for the input image. + * + * @param kImage ModelImage. + */ + private void initLUT() { + + if (m_kImage.isColorImage()) { + final float[] x = new float[4]; + final float[] y = new float[4]; + final Dimension dim = new Dimension(256, 256); + + // Set ModelRGB min max values; + x[0] = 0; + y[0] = dim.height - 1; + + x[1] = 255 * 0.333f; + y[1] = (dim.height - 1) - ( (dim.height - 1) / 3.0f); + + x[2] = 255 * 0.667f; + y[2] = (dim.height - 1) - ( (dim.height - 1) * 0.67f); + + x[3] = 255; + y[3] = 0; + + final int[] RGBExtents = new int[2]; + RGBExtents[0] = 4; + RGBExtents[1] = 256; + m_kRGBT = new ModelRGB(RGBExtents); + m_kRGBT.getRedFunction().importArrays(x, y, 4); + m_kRGBT.getGreenFunction().importArrays(x, y, 4); + m_kRGBT.getBlueFunction().importArrays(x, y, 4); + m_kRGBT.makeRGB( -1); + } else { + final int[] dimExtentsLUT = new int[2]; + + dimExtentsLUT[0] = 4; + dimExtentsLUT[1] = 256; + + m_kLUT = new ModelLUT(ModelLUT.GRAY, 256, dimExtentsLUT); + + float min, max; + + if (m_kImage.getType() == ModelStorageBase.UBYTE) { + min = 0; + max = 255; + } else if (m_kImage.getType() == ModelStorageBase.BYTE) { + min = -128; + max = 127; + } else { + min = (float) m_kImage.getMin(); + max = (float) m_kImage.getMax(); + } + + final float imgMin = (float) m_kImage.getMin(); + final float imgMax = (float) m_kImage.getMax(); + + m_kLUT.resetTransferLine(min, imgMin, max, imgMax); + } + } + + /** + * Initialize the scale factors. Based on the ModelImage Volume. + */ + private void InitScale() { + + int dimX = m_kImage.getExtents().length > 0 ? m_kImage.getExtents()[0] : 1; + int dimY = m_kImage.getExtents().length > 1 ? m_kImage.getExtents()[1] : 1; + int dimZ = m_kImage.getExtents().length > 2 ? m_kImage.getExtents()[2] : 1; + m_iMaxExtent = Math.max( dimX, Math.max( dimY, dimZ ) ); + + final float fMaxX = (m_kImage.getExtents()[0] - 1) * m_kImage.getFileInfo(0).getResolutions()[0]; + final float fMaxY = (m_kImage.getExtents()[1] - 1) * m_kImage.getFileInfo(0).getResolutions()[1]; + final float fMaxZ = (m_kImage.getExtents()[2] - 1) * m_kImage.getFileInfo(0).getResolutions()[2]; + + m_fMax = fMaxX; + if (fMaxY > m_fMax) { + m_fMax = fMaxY; + } + if (fMaxZ > m_fMax) { + m_fMax = fMaxZ; + } + m_fX = fMaxX / m_fMax; + m_fY = fMaxY / m_fMax; + m_fZ = fMaxZ / m_fMax; + } + + /** + * Reads an image from disk. + * + * @param kImageName image name + * @param kDir directory + * @return ModelImage + */ + private static ModelImage ReadFromDisk(final String kImageName, final String kDir) { + + final File kFile = new File(kDir, kImageName); + if ( !kFile.exists()) { + return null; + } + + final FileIO fileIO = new FileIO(); + return fileIO.readImage( kImageName, kDir ); + //return fileIO.readXML(kImageName + ".xml", kDir, false, false); + } + + private void readObject(final java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { + m_kDir = (String) in.readObject(); + if ( !m_kDir.equals("null")) { + final String kImageName = (String) in.readObject(); + m_kPostfix = (String) in.readObject(); + m_kImage = ReadFromDisk(kImageName, m_kDir); + init(null, 0, true); + } + } + + + /** + * Go to the next 3D volume sub-image for the 4D animation. + * Updates the Textures and causes them to be reloaded onto the GPU. + */ + private void update4D() { + if ( m_kImage == null ) return; + + m_kVolumeTarget.SetImage(m_kVolume[m_iTimeSlice]); + m_kVolumeTarget.Reload(true); + if ( m_bGMInit ) + { + if ( m_kVolumeGM[m_iTimeSlice] == null ) + { + ModelImage gmImage = getGradientMagnitude( m_kImage, m_iTimeSlice ); + m_akGradientMagMinMax[m_iTimeSlice] = new Vector2f( (float)gmImage.getMin(), (float)gmImage.getMax() ); + gmImage.disposeLocal(); + m_kVolumeGMTarget.SetImage(createGM_Laplace(gmImage, null, null, 0, true)); + m_kVolumeGMTarget.Reload(true); + } + else + { + m_kVolumeGMTarget.SetImage(m_kVolumeGM[m_iTimeSlice]); + m_kVolumeGMTarget.Reload(true); + } + } + if ( m_bNormalsInit && m_kImage.isColorImage() ) + { + m_kNormalMapTarget.SetImage(m_kNormal[m_iTimeSlice]); + m_kNormalMapTarget.Reload(true); + } + if ( m_bHistoInit && (m_kHisto[m_iTimeSlice] != null )) + { + m_kHistoTarget.SetImage(m_kHisto[m_iTimeSlice]); + m_kHistoTarget.Reload(true); + } + + m_kImage.setTimeSlice(m_iTimeSlice); + } + + /** + * Called when the opacity transfer function changes. This function updates the Texture + * and causes the data to be reloaded onto the GPU. + * + * @param kImage the ModelImage the transfer function applies to. + * @param kOpacityTexture the opacity Texture passed to the GPU + * @param kOpacityMap the opacity data stored in the GraphicsImage + * @param kTransfer the new transfer function. + */ + private boolean UpdateImages(final ModelImage kImage, final Texture kOpacityTexture, + final GraphicsImage kOpacityMap, final TransferFunction kTransfer) { + final int iLutHeight = 256; + final float[] afData = kOpacityMap.GetFloatData(); + + final float fRange = (float) (kImage.getMax() - kImage.getMin()); + final float fStep = fRange / iLutHeight; + float fDataValue = (float) kImage.getMin(); + for (int i = 0; i < iLutHeight; i++) { + afData[i] = (kTransfer.getRemappedValue(fDataValue, iLutHeight) / 255.0f); + fDataValue += fStep; + } + kOpacityTexture.Reload(true); + return true; + } + + public TransferFunction getOpacityFn() { + return opacityTransferFn; + } + + /** + * Update the opacity transfer function. + * + * @param kImage the ModelImage the transfer function applies to. + * @param kOpacityTexture the opacity Texture passed to the GPU + * @param kOpacityMap the opacity data stored in the GraphicsImage + * @param kTransfer the new transfer function. + */ + private boolean UpdateImages2(final ModelImage kImage, final Texture kOpacityTexture, + final GraphicsImage kOpacityMap, final TransferFunction kTransfer) { + opacityTransferFn = new TransferFunction(kTransfer); + final int iLutHeight = kOpacityMap.GetBound(0); + final byte[] abData = kOpacityMap.GetData(); + + final float fRange = (float) (kImage.getMax() - kImage.getMin()); + final float fStep = fRange / iLutHeight; + float fDataValue = (float) kImage.getMin(); + float fVal; + for (int i = 0; i < iLutHeight; i++) { + fVal = (kTransfer.getRemappedValue(fDataValue, iLutHeight) / 255.0f); + abData[i * 4 + 3] = (byte) (fVal * 255); + fDataValue += fStep; + } + kOpacityTexture.Reload(true); + return true; + } + + private void writeObject(final java.io.ObjectOutputStream out) throws IOException { + if (m_kImage != null) { + out.writeObject(m_kDir); + out.writeObject(m_kImage.getImageFileName()); + out.writeObject(m_kPostfix); + m_kImage.saveImage(m_kDir, m_kImage.getImageFileName(), FileUtility.XML, false, false); + } else { + out.writeObject("null"); + } + } +} From 9c6ec522eb8fb17b8f58e36b856bccd7b6639351 Mon Sep 17 00:00:00 2001 From: chend Date: Tue, 14 May 2024 16:55:01 -0400 Subject: [PATCH 11/39] Added updateSelectionPanel to fix the issue of clicking accurateMode between 2 different images side by side --- .../VOI/VOILatticeManagerInterface.java | 19 ++++++++++++------- .../PlugInDialogVolumeRenderDualJanelia.java | 14 ++++++++++++-- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/gov/nih/mipav/view/renderer/WildMagic/VOI/VOILatticeManagerInterface.java b/src/gov/nih/mipav/view/renderer/WildMagic/VOI/VOILatticeManagerInterface.java index 58201fe4f2..933e948e85 100644 --- a/src/gov/nih/mipav/view/renderer/WildMagic/VOI/VOILatticeManagerInterface.java +++ b/src/gov/nih/mipav/view/renderer/WildMagic/VOI/VOILatticeManagerInterface.java @@ -1133,18 +1133,23 @@ public boolean isAccurateMode() { return accurateMode; } - public void toggleAccurateMode() { - accurateMode = !accurateMode; - for (AccurateModeListener listener : listeners) { - listener.accurateModeChanged(accurateMode); - - } - } + public void toggleAccurateMode() { + accurateMode = !accurateMode; + setAccurateMode(accurateMode); + } + + public void setAccurateMode(boolean accurateMode) { + this.accurateMode = accurateMode; + for (AccurateModeListener listener : listeners) { + listener.accurateModeChanged(accurateMode); + } + } public void addAccurateModeListener(AccurateModeListener listener) { listeners.add(listener); } + public void keyReleased(KeyEvent e) { isShiftSelected = e.isShiftDown(); movingPickedPoint = false; diff --git a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java index 7b45b43a02..88f80e81a3 100644 --- a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java +++ b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java @@ -1007,6 +1007,8 @@ else if ( activeImage.latticeTwisted != null ) { updateHistoLUTPanels(activeImage); updateClipPanel(activeImage, activeRenderer, true); updateSurfacePanels(); + updateSelectionPanel(); + if (activeImage.currentTab != -1) { tabbedPane.setSelectedIndex(activeImage.currentTab); @@ -1074,6 +1076,7 @@ public void setActiveRenderer(VolumeTriPlanarRenderBase renderer) { updateSurfacePanels(); updateClipPanel(activeImage, activeRenderer, true); updateHistoLUTPanels(activeImage); + updateSelectionPanel(); if (editMode == EditLattice) { leftImage.voiManager.editLattice(); @@ -1326,9 +1329,9 @@ public boolean updateImages(ModelLUT LUTa, ModelLUT LUTb, boolean flag, int inte } - @Override + @Override public void accurateModeChanged(boolean isAccurateMode) { - accurateModeButton.setSelected(isAccurateMode); + accurateModeButton.setSelected(isAccurateMode); accurateModeButton.setText(isAccurateMode ? "Accurate Mode is now On" : "Accurate Mode is now Off"); } @@ -3333,6 +3336,13 @@ private void updateSurfacePanels() { // tabbedPane.addTab("Surface", null, lightsPanel); // } } + + private void updateSelectionPanel() { + // accurateModeChanged(activeImage.voiManager.isAccurateMode()); + + VOILatticeManagerInterface voiManager = activeImage.voiManager; + voiManager.setAccurateMode(voiManager.isAccurateMode()); + } /* * private VOIVector autoLattice() { VOIVector latticeContainer = new From fd0980b21a7d0ee8d9b917fb9255d018b82ab967 Mon Sep 17 00:00:00 2001 From: chend Date: Wed, 15 May 2024 16:45:33 -0400 Subject: [PATCH 12/39] Added block comments --- .../worm/untwisting/AccurateModeListener.java | 17 +++++++++++++++-- .../PlugInAlgorithmWormUntwistingJanelia.java | 11 +++++++---- .../PlugInDialogVolumeRenderDualJanelia.java | 2 +- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/org/janelia/mipav/plugins/worm/untwisting/AccurateModeListener.java b/src/org/janelia/mipav/plugins/worm/untwisting/AccurateModeListener.java index 2b59754ceb..61dba10b75 100644 --- a/src/org/janelia/mipav/plugins/worm/untwisting/AccurateModeListener.java +++ b/src/org/janelia/mipav/plugins/worm/untwisting/AccurateModeListener.java @@ -1,7 +1,20 @@ package org.janelia.mipav.plugins.worm.untwisting; - +/** + * The listener interface for receiving changes to accurateMode. + * The class that is interested in processing an action event + * implements this interface, and the object created with that + * class is registered with a component, using the component's + * {@code addAccurateModeListener} method. When the action event + * occurs, that object's {@code accurateModeChanged} method is + * invoked. + * + * @author diyi chen + */ public interface AccurateModeListener { - // Add this method for responding to the accurateModeListener + /** + * Add this method for responding to the accurateModeListener + * @param isAccurateMode true if the state of the mode is accurate + */ void accurateModeChanged(boolean isAccurateMode); } \ No newline at end of file diff --git a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInAlgorithmWormUntwistingJanelia.java b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInAlgorithmWormUntwistingJanelia.java index 3e41fe34f0..eb2f05eb54 100644 --- a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInAlgorithmWormUntwistingJanelia.java +++ b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInAlgorithmWormUntwistingJanelia.java @@ -494,17 +494,20 @@ public static void batchFlipLattices( JProgressBar batchProgress, final Vector includeRange, final String[] baseFileDir, final String baseFileName, final String outputDir, final int paddingFactor, final boolean segmentLattice) { diff --git a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java index 88f80e81a3..2d10a29c91 100644 --- a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java +++ b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java @@ -1114,7 +1114,7 @@ public void dispose() { * lattice, or annotations. The next step in the straightening process is * automatically enabled and selected. * - * @param mode + * @param mode TODO */ public void enableNext(int mode) { closeAll(); From ffc8804da7f02144115f4e21555da096d85a4a86 Mon Sep 17 00:00:00 2001 From: chend Date: Wed, 22 May 2024 10:04:02 -0400 Subject: [PATCH 13/39] Created a plot corresponding to each clicking reflects the maximum intensity. --- .../WildMagic/VolumeTriPlanarRender.java | 53 +- .../plugins/worm/untwisting/PlotListener.java | 27 + .../PlugInDialogVolumeRenderDualJanelia.java | 755 ++++++++++-------- 3 files changed, 484 insertions(+), 351 deletions(-) create mode 100644 src/org/janelia/mipav/plugins/worm/untwisting/PlotListener.java diff --git a/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java b/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java index e4857baed1..3d7c722c48 100644 --- a/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java +++ b/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java @@ -53,7 +53,11 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import java.util.Vector; import com.jogamp.opengl.GLAutoDrawable; @@ -61,6 +65,9 @@ import com.jogamp.opengl.awt.GLCanvas; import javax.swing.KeyStroke; +import org.janelia.mipav.plugins.worm.untwisting.AccurateModeListener; +import org.janelia.mipav.plugins.worm.untwisting.PlotListener; +import org.janelia.mipav.plugins.worm.untwisting.PlugInDialogVolumeRenderDualJanelia; import org.janelia.mipav.test.ValueOutput; import WildMagic.LibFoundation.Mathematics.ColorRGBA; @@ -846,8 +853,13 @@ else if ( m_bErase ) m_bMouseDrag = false; } + + private void PickVolume3D(Vector3f kPos, Vector3f kDir, Vector3f maxPtAccurate) { + List plotAccurateValues = new ArrayList<>(); + List plotValues = new ArrayList<>(); + m_kPicker.Execute(m_kVolumeRayCast.GetScene(),kPos,kDir,0.0f, Float.MAX_VALUE); @@ -956,6 +968,7 @@ private void PickVolume3D(Vector3f kPos, Vector3f kDir, Vector3f maxPtAccurate) maxValueAccurate = valueAccurate; maxPtAccurate.copy(p0); } + plotAccurateValues.add(valueAccurate); // Write data to CSV outputAccurate.writeData(p0.X, p0.Y, p0.Z, valueAccurate); @@ -990,10 +1003,12 @@ private void PickVolume3D(Vector3f kPos, Vector3f kDir, Vector3f maxPtAccurate) maxValue = value; maxPt.copy(p0); } + plotValues.add(value); output.writeData(p0.X, p0.Y, p0.Z, value); } } + output.writeData(maxPt.X, maxPt.Y, maxPt.Z, maxValue); outputAccurate.writeData(maxPtAccurate.X, maxPtAccurate.Y, maxPtAccurate.Z, maxValueAccurate); @@ -1056,16 +1071,38 @@ else if ( !doAutomaticLabels() ) m_kVOIInterface.updateDisplay(); } } - } -} + } + if (m_kVOIInterface.isAccurateMode()) { + setPlot(plotAccurateValues, "Accurate Values"); + } else { + setPlot(plotValues, "3-Color Values"); + } + } + + // Create a Set to hold PlotListener instances + private Set listeners = new HashSet<>(); + + // update all listeners with new plot values and title. + public void setPlot(List values, String title) { + for (PlotListener listener : listeners) { + listener.updatePlotPanel(values, title); + } + } + + // add a new PlotListener + public void addPlotListener(PlotListener listener) { + listeners.add(listener); + } - private boolean PickSlice3D(Vector3f kPos, Vector3f kDir, Vector3f maxPt) - { + // remove an existing PlotListener + public void removePlotListener(PlotListener listener) { + listeners.remove(listener); + } + + private boolean PickSlice3D(Vector3f kPos, Vector3f kDir, Vector3f maxPt) { // pick on the slices - m_kPicker.Execute(m_kSlices.GetScene(),kPos,kDir,0.0f, - Float.MAX_VALUE); - if (m_kPicker.Records.size() > 0) - { + m_kPicker.Execute(m_kSlices.GetScene(), kPos, kDir, 0.0f, Float.MAX_VALUE); + if (m_kPicker.Records.size() > 0) { PickRecord kPickPoint = m_kPicker.GetClosestNonnegative(); TriMesh kMesh = (TriMesh)kPickPoint.Intersected; int iPlane = m_kSlices.whichPlane(kMesh); diff --git a/src/org/janelia/mipav/plugins/worm/untwisting/PlotListener.java b/src/org/janelia/mipav/plugins/worm/untwisting/PlotListener.java new file mode 100644 index 0000000000..b080398fa8 --- /dev/null +++ b/src/org/janelia/mipav/plugins/worm/untwisting/PlotListener.java @@ -0,0 +1,27 @@ +package org.janelia.mipav.plugins.worm.untwisting; + +import java.util.List; + +/** + * Interface for handling updates to a plot panel. Classes that need to respond + * to plot data changes should implement this interface. An instance of a class + * implementing this interface can be registered with components that generate + * plot data updates. When plot data changes occur, the {@code updatePlotPanel} + * method of each registered listener is called, allowing the implementing class + * to update the plot display accordingly. + * + * @author diyi chen + */ +public interface PlotListener { + + /** + * Invoked to update the plot panel with new data. + * + * @param values The list of data values to plot. Each value represents a data + * point on the plot. + * @param title The title of the plot, describing the data or context of what + * is being displayed + */ + void updatePlotPanel(List values, String title); + +} diff --git a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java index 2d10a29c91..7e56055a31 100644 --- a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java +++ b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java @@ -1,6 +1,5 @@ package org.janelia.mipav.plugins.worm.untwisting; - //MIPAV is freely available from http://mipav.cit.nih.gov //THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, @@ -68,11 +67,13 @@ import static org.jocl.CL.CL_DEVICE_TYPE_GPU; +import java.awt.BasicStroke; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; +import java.awt.Graphics2D; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.GridLayout; @@ -82,6 +83,7 @@ import java.awt.event.ActionListener; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; +import java.awt.image.BufferedImage; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.BufferedReader; @@ -112,9 +114,20 @@ import javax.swing.JTabbedPane; import javax.swing.JTextField; import javax.swing.JToggleButton; +import javax.swing.SwingUtilities; +import javax.swing.border.TitledBorder; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; - +import javax.swing.ImageIcon; + +import org.janelia.mipav.test.Plot; +import org.jfree.chart.ChartFactory; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; +import org.jfree.data.xy.XYSeries; +import org.jfree.data.xy.XYSeriesCollection; import org.jocl.Sizeof; import WildMagic.LibFoundation.Mathematics.Mathf; @@ -129,14 +142,15 @@ import WildMagic.LibGraphics.SceneGraph.TriMesh; /** -* Implements the user-interface for the semi-automatic straightening of the -* worm. Provides batch-process algorithms for segmenting the seam cells and -* building lattices. Provides the framework for enabling the user to step -* through the selected image volumes and view/edit results from the automatic -* processes. Provides framework for animating the annotations after untwisting. -*/ -public class PlugInDialogVolumeRenderDualJanelia extends JFrame implements ActionListener, RendererListener, - PropertyChangeListener, ViewImageUpdateInterface, WindowListener, ChangeListener, AccurateModeListener { + * Implements the user-interface for the semi-automatic straightening of the + * worm. Provides batch-process algorithms for segmenting the seam cells and + * building lattices. Provides the framework for enabling the user to step + * through the selected image volumes and view/edit results from the automatic + * processes. Provides framework for animating the annotations after untwisting. + */ +public class PlugInDialogVolumeRenderDualJanelia extends JFrame + implements ActionListener, RendererListener, PropertyChangeListener, ViewImageUpdateInterface, WindowListener, + ChangeListener, AccurateModeListener, PlotListener { private static final long serialVersionUID = -9056581285643263551L; @@ -217,7 +231,7 @@ public class PlugInDialogVolumeRenderDualJanelia extends JFrame implements Actio private JTabbedPane opacityTab; private JPanel clipPanel; private JPanel accuratePanel; - //private JButton accurateModeButton; + // private JButton accurateModeButton; private JToggleButton accurateModeButton; private PlugInDialogVolumeRenderDualJanelia parent; @@ -311,15 +325,15 @@ public PlugInDialogVolumeRenderDualJanelia() { public void actionPerformed(ActionEvent event) { String command = event.getActionCommand(); Object source = event.getSource(); - - if ( command.equals("BrowseConclude") ) { + + if (command.equals("BrowseConclude")) { UntwistDialog inputs = new UntwistDialog(baseFileLocText.getText()); - if ( inputs.latticeOutputDir != null ) { + if (inputs.latticeOutputDir != null) { latticeFileDir = new String(inputs.latticeOutputDir.getText()); setDefaultInputList(latticeFileDir); } } - + if (currentSize == null) { currentSize = new Dimension(getSize()); System.err.println(currentSize); @@ -334,8 +348,7 @@ public void actionPerformed(ActionEvent event) { this.setVisible(false); annotationAnimationFromSpreadSheet(); return; - } - else { + } else { // this.setVisible(false); // testForceGraph(); MipavUtil.displayError("Please specify a range of images."); @@ -346,8 +359,8 @@ public void actionPerformed(ActionEvent event) { if (batchFlipLattice.isSelected()) { try { // Batch Automatic Lattice-Building - PlugInAlgorithmWormUntwistingJanelia.batchFlipLattices(batchProgress, includeRange, latticeFileDir, - baseFileNameText.getText()); + PlugInAlgorithmWormUntwistingJanelia.batchFlipLattices(batchProgress, includeRange, + latticeFileDir, baseFileNameText.getText()); } catch (java.lang.OutOfMemoryError e) { MipavUtil .displayError("Error: Not enough memory. Unable to finish automatic lattice-building."); @@ -382,8 +395,7 @@ public void actionPerformed(ActionEvent event) { try { if (resliceImageCheck.isSelected()) { PlugInAlgorithmWormUntwistingJanelia.reslice(batchProgress, includeRange, baseFileDir, - baseFileNameText.getText(), resliceXValue, resliceYValue, - resliceZValue); + baseFileNameText.getText(), resliceXValue, resliceYValue, resliceZValue); } } catch (java.lang.OutOfMemoryError e) { MipavUtil.displayError("Error: Not enough memory. Unable to finish reslice calculation."); @@ -412,7 +424,8 @@ public void actionPerformed(ActionEvent event) { validate(); leftRenderer.setVisible(true); - if ( rightRenderer != null ) rightRenderer.setVisible(true); + if (rightRenderer != null) + rightRenderer.setVisible(true); } else { editMode = EditNONE; startButton.setEnabled(true); @@ -426,12 +439,13 @@ public void actionPerformed(ActionEvent event) { editCrossSections.setVisible(true); predict.setVisible(true); previewUntwisting.setVisible(true); - displayControls.setVisible(false); + displayControls.setVisible(false); imageChannels.setVisible(true); validate(); leftRenderer.setVisible(true); - if ( rightRenderer != null ) rightRenderer.setVisible(true); + if (rightRenderer != null) + rightRenderer.setVisible(true); } else { editMode = EditNONE; startButton.setEnabled(true); @@ -449,7 +463,8 @@ public void actionPerformed(ActionEvent event) { validate(); leftRenderer.setVisible(true); - if ( rightRenderer != null ) rightRenderer.setVisible(true); + if (rightRenderer != null) + rightRenderer.setVisible(true); } else { editMode = EditNONE; startButton.setEnabled(true); @@ -552,12 +567,12 @@ public void actionPerformed(ActionEvent event) { // Closes the editing: else if (command.equals("done")) { if (!displayControls.isVisible()) { - displayControls.setVisible(true); + displayControls.setVisible(true); imageChannels.setVisible(false); validate(); } - //mkitti 2023_03_01: this is where we save when "done" + // mkitti 2023_03_01: this is where we save when "done" saveAll(); if (parent != null) { parent.enableNext(editMode); @@ -566,10 +581,9 @@ else if (command.equals("done")) { } else { enableNext(editMode); } - } - else if (command.equals("demo")) { + } else if (command.equals("demo")) { updateSurfacePanels(); - activeRenderer.addAnimationLattice( activeImage.voiManager.getLattice() ); + activeRenderer.addAnimationLattice(activeImage.voiManager.getLattice()); } // Enables user to generate a new lattice (when none of the automatic ones match // well) @@ -645,16 +659,15 @@ else if (command.equals("newLattice")) { // activeImage.opacity[i] = activeImage.volOpacityPanel[i].getCompA().getOpacityTransferFunction(); //// activeImage.hyperstack[i].UpdateImages(activeImage.opacity[i], 0, null); // } - + if (previewUntwisting.getText().equals("preview")) { previewUntwisting.setText("return"); - + previewCount++; - + // save relative contours here upon returning from preview activeImage.voiManager.saveCrossSections(); - // save image orientation: activeImage.volumeMatrix = new Matrix3f(activeRenderer.GetSceneRotation()); @@ -665,28 +678,25 @@ else if (command.equals("newLattice")) { activeImage.annotationsTwisted = null; } activeImage.latticeTwisted = new VOIVector(activeImage.voiManager.getLattice()); - - if (activeImage.previewHS != null) { - for ( int i = 0; i < activeImage.previewHS.length; i++ ) { + for (int i = 0; i < activeImage.previewHS.length; i++) { activeImage.previewHS[i].GetImage().disposeLocal(false); } } activeImage.previewHS = untwistingTest(); - for ( int i = 0; i < activeImage.previewHS.length; i++ ) { - activeImage.previewHS[i].UpdateImages( activeImage.hyperstack[i].GetLUT()); + for (int i = 0; i < activeImage.previewHS.length; i++) { + activeImage.previewHS[i].UpdateImages(activeImage.hyperstack[i].GetLUT()); } activeImage.voiManager.removeListeners(); activeImage.voiManager.setImage(activeImage.previewHS[0].GetImage(), null); activeImage.volumeImage = activeImage.previewHS[0]; activeRenderer.setHyperStack(activeImage.previewHS, activeImage.colormap); - } - else { + } else { // "return" previewCount--; previewUntwisting.setText("preview"); - + // save relative contours here upon returning from preview activeImage.voiManager.saveCrossSections(); @@ -694,8 +704,6 @@ else if (command.equals("newLattice")) { activeImage.voiManager.setImage(activeImage.hyperstack[0].GetImage(), null); activeImage.volumeImage = activeImage.hyperstack[0]; - - // restore twisted annotations and lattice: activeImage.wormImage.unregisterAllVOIs(); VOIVector newLatticeTwisted = activeImage.voiManager.retwistLattice(activeImage.latticeTwisted); @@ -745,18 +753,15 @@ else if (command.equals("newLattice")) { } } } - - - + if (activeImage.previewHS != null) { - for ( int i = 0; i < activeImage.previewHS.length; i++ ) { + for (int i = 0; i < activeImage.previewHS.length; i++) { activeImage.previewHS[i].GetImage().disposeLocal(false); } activeImage.previewHS = null; } activeRenderer.setHyperStack(activeImage.hyperstack, activeImage.colormap); - - + // activeImage.voiManager.setImage(activeImage.wormImage, null); // activeImage.volumeImage.UpdateData(activeImage.wormImage, activeImage.volumeImage.GetLUT(), true); // activeRenderer.resetAxis(); @@ -823,44 +828,44 @@ else if (command.equals("newLattice")) { if (activeImage.voiManager != null) { activeImage.voiManager.setPaddingFactor(paddingFactor); } - } - - if ( source instanceof JCheckBox ) { - boolean selected = ((JCheckBox)source).isSelected(); - int which = imageChannel(command); + } + + if (source instanceof JCheckBox) { + boolean selected = ((JCheckBox) source).isSelected(); + int which = imageChannel(command); // System.err.println(which + " " + selected ); - if ( which != -1 ) { - activeRenderer.setImageOn(which, selected); + if (which != -1) { + activeRenderer.setImageOn(which, selected); } - if(opacityTab != null && opacityTab.getTabCount() > 0) { + if (opacityTab != null && opacityTab.getTabCount() > 0) { which = opacityTab.getSelectedIndex(); - for(int i = 0; i < opacityTab.getTabCount(); ++i) { - if( opacityTab.getTitleAt(i).startsWith(command) ) { + for (int i = 0; i < opacityTab.getTabCount(); ++i) { + if (opacityTab.getTitleAt(i).startsWith(command)) { opacityTab.setEnabledAt(i, selected); - if( !selected && i == which) { + if (!selected && i == which) { opacityTab.setSelectedIndex((which + 1) % opacityTab.getTabCount()); } - if( selected ) { + if (selected) { opacityTab.setSelectedIndex(i); } } } } - if(lutTab != null && lutTab.getTabCount() > 0) { + if (lutTab != null && lutTab.getTabCount() > 0) { which = lutTab.getSelectedIndex(); - for(int i = 0; i < lutTab.getTabCount(); ++i) { - if( lutTab.getTitleAt(i).startsWith(command) ) { + for (int i = 0; i < lutTab.getTabCount(); ++i) { + if (lutTab.getTitleAt(i).startsWith(command)) { lutTab.setEnabledAt(i, selected); - if( !selected && i == which) { + if (!selected && i == which) { lutTab.setSelectedIndex((which + 1) % lutTab.getTabCount()); } - if ( selected ) { + if (selected) { lutTab.setSelectedIndex(i); } } } } - + } } @@ -882,7 +887,6 @@ private void checkAnnotations() { } } - private String resultsDir(String sharedDir, ModelImage image) { String imageName = image.getImageFileName(); if (imageName.contains("_clone")) { @@ -891,13 +895,13 @@ private String resultsDir(String sharedDir, ModelImage image) { if (imageName.contains("_straight")) { imageName = imageName.replaceAll("_straight", ""); } - String outputDir = sharedDir + File.separator + JDialogBase.makeImageName(imageName, "") + - File.separator + JDialogBase.makeImageName(imageName, "_results") + File.separator; + String outputDir = sharedDir + File.separator + JDialogBase.makeImageName(imageName, "") + File.separator + + JDialogBase.makeImageName(imageName, "_results") + File.separator; return outputDir; } - + public void rendererConfigured(VolumeTriPlanarRenderBase renderer) { - + if ((annotationList != null) && (annotationNames != null) && (triVolume != null)) { triVolume.addVOIS(annotationList, annotationNames); triVolume.displayAnnotationSpheres(); @@ -918,6 +922,7 @@ public void rendererConfigured(VolumeTriPlanarRenderBase renderer) { leftDisplayPanel.setBorder(JDialogBase.buildTitledBorder(leftImage.wormImage.getImageName())); rightDisplayPanel.setBorder(JDialogBase.buildTitledBorder(rightImage.wormImage.getImageName(), Color.red)); } + activeRenderer.addPlotListener(this); if (activeImage.voiManager == null) { activeImage.voiManager = new VOILatticeManagerInterface(null, activeRenderer.getVolumeImage().GetImage(), @@ -925,29 +930,26 @@ public void rendererConfigured(VolumeTriPlanarRenderBase renderer) { } activeRenderer.setVOILatticeManager(activeImage.voiManager); - if ( activeImage.previewHS != null ) { + if (activeImage.previewHS != null) { activeImage.voiManager.setPreviewMode(true, activeImage.voiManager.getLatticeStraight(), activeImage.voiManager.getAnnotationsStraight()); activeRenderer.resetAxisXInv(); - } - else if ( activeImage.latticeTwisted != null ) { - activeImage.voiManager.setPreviewMode(false, activeImage.latticeTwisted, - activeImage.annotationsTwisted); + } else if (activeImage.latticeTwisted != null) { + activeImage.voiManager.setPreviewMode(false, activeImage.latticeTwisted, activeImage.annotationsTwisted); activeRenderer.SetSceneRotation(activeImage.volumeMatrix); - } - else { - if (editMode == ReviewResults) { - activeImage.voiManager.setLattice(WormData.readStraightLattice( resultsDir(latticeFileDir, activeImage.wormImage))); - } - else { + } else { + if (editMode == ReviewResults) { + activeImage.voiManager + .setLattice(WormData.readStraightLattice(resultsDir(latticeFileDir, activeImage.wormImage))); + } else { if (loadLegacyLatticeCheck.isSelected()) { activeImage.wormImage.setResolutions(originalResolutions); } final String dir = resultsDir(latticeFileDir, activeImage.wormImage); - activeImage.voiManager - .setLattice(WormData.readFinalLattice(dir, - loadLegacyLatticeCheck.isSelected(), activeImage.wormImage), dir); + activeImage.voiManager.setLattice( + WormData.readFinalLattice(dir, loadLegacyLatticeCheck.isSelected(), activeImage.wormImage), + dir); if (loadLegacyLatticeCheck.isSelected()) { activeImage.wormImage.setResolutions(new float[] { 1f, 1f, 1 }); @@ -961,17 +963,19 @@ else if ( activeImage.latticeTwisted != null ) { activeImage.annotations = null; } VOI annotations = activeImage.voiManager.getAnnotations(); - // System.err.println( activeImage.wormImage.getImageName() + " " + annotations); + // System.err.println( activeImage.wormImage.getImageName() + " " + + // annotations); if (annotations != null) { for (int i = 0; i < annotations.getCurves().size(); i++) { final VOIText text = (VOIText) annotations.getCurves().elementAt(i); - // System.err.println( " " + i + " " + text.getText() + " " + text.elementAt(0)); + // System.err.println( " " + i + " " + text.getText() + " " + + // text.elementAt(0)); text.createVolumeVOI(activeImage.volumeImage, activeRenderer.getTranslate()); } } } - + activeRenderer.displayVOIs(true); activeImage.voiManager.editAnnotations(editMode == EditSeamCells); activeImage.voiManager.colorAnnotations(editMode == EditSeamCells); @@ -983,7 +987,7 @@ else if ( activeImage.latticeTwisted != null ) { activeImage.annotationPanelUI.setPreviewMode(activeImage.previewHS != null); activeImage.latticeTable.setPreviewMode(activeImage.previewHS != null); - + activeImage.voiManager.editAnnotations(false); // initDisplaySeamPanel(); } else if (editMode == EditLattice) { @@ -1002,18 +1006,16 @@ else if ( activeImage.latticeTwisted != null ) { renderer.resetAxisXInv(); } - initImageChannels(activeImage); updateHistoLUTPanels(activeImage); updateClipPanel(activeImage, activeRenderer, true); updateSurfacePanels(); updateSelectionPanel(); - if (activeImage.currentTab != -1) { tabbedPane.setSelectedIndex(activeImage.currentTab); } - + int nextStep = dualGPU == null ? 1 : 2; imageIndex = Math.min(includeRange.size() - 1, imageIndex); imageIndex = Math.max(0, imageIndex); @@ -1027,7 +1029,6 @@ else if ( activeImage.latticeTwisted != null ) { backButton.setEnabled(false); nextButton.setEnabled(false); } - setExtendedState(JFrame.MAXIMIZED_BOTH); if (dualGPU != null) { @@ -1048,7 +1049,7 @@ else if ( activeImage.latticeTwisted != null ) { public void setActiveRenderer(VolumeTriPlanarRenderBase renderer) { // System.err.println("setActiveRenderer"); - VolumeTriPlanarRenderBase previousActive = activeRenderer; + VolumeTriPlanarRender previousActive = activeRenderer; if (leftRenderer == renderer) { activeRenderer = leftRenderer; leftDisplayPanel.setBorder(JDialogBase.buildTitledBorder(leftImage.wormImage.getImageName(), Color.red)); @@ -1062,7 +1063,8 @@ public void setActiveRenderer(VolumeTriPlanarRenderBase renderer) { activeImage = rightImage; } - + activeRenderer.addPlotListener(this); + if (previousActive != activeRenderer) { if (activeImage.voiManager.isPreview()) { @@ -1086,6 +1088,8 @@ public void setActiveRenderer(VolumeTriPlanarRenderBase renderer) { tabbedPane.setSelectedIndex(activeImage.currentTab); stateChanged(null); } + previousActive.removePlotListener(this); + } int nextStep = dualGPU == null ? 1 : 2; if (includeRange != null) { @@ -1204,11 +1208,11 @@ private void closeAll() { if (imageStack[i].voiManager != null) { imageStack[i].voiManager = null; } - if ( imageStack[i].hyperstack != null ) { - for ( int j = 0; j < imageStack[i].hyperstack.length; j++ ) { - if ( imageStack[i].hyperstack[j] != null ) { + if (imageStack[i].hyperstack != null) { + for (int j = 0; j < imageStack[i].hyperstack.length; j++) { + if (imageStack[i].hyperstack[j] != null) { imageStack[i].hyperstack[j].dispose(); - imageStack[i].hyperstack[j] = null; + imageStack[i].hyperstack[j] = null; } } } @@ -1237,8 +1241,9 @@ public void propertyChange(PropertyChangeEvent event) { if (propertyName.equals("Opacity")) { int which = opacityTab.getSelectedIndex(); // System.err.println("propertyChange " + which); - if ( which != -1 ) { - final TransferFunction kTransfer = activeImage.volOpacityPanel[which].getCompA().getOpacityTransferFunction(); + if (which != -1) { + final TransferFunction kTransfer = activeImage.volOpacityPanel[which].getCompA() + .getOpacityTransferFunction(); updateImages(activeImage.colormap, activeImage.hyperstack[which].GetImage(), kTransfer, which); activeImage.hyperstack[which].UpdateImages(kTransfer, 0, null); // @@ -1267,8 +1272,8 @@ public boolean updateImages() { // System.err.println("updateImages" ); if (activeImage.hyperstack != null && lutTab != null) { int which = lutTab.getSelectedIndex(); - if ( which != -1 ) { - updateImages(activeImage.colormap, activeImage.hyperstack[which].GetLUT(), which ); + if (which != -1) { + updateImages(activeImage.colormap, activeImage.hyperstack[which].GetLUT(), which); // System.err.println("updateImages " + which); activeImage.hyperstack[which].UpdateImages(activeImage.hyperstack[which].getLUT()); // if ( activeImage.previewHS != null ) { @@ -1281,14 +1286,14 @@ public boolean updateImages() { } return false; } - - private void updateImages(Texture texture, ModelLUT lut, int index ) { + + private void updateImages(Texture texture, ModelLUT lut, int index) { byte[] data = texture.GetImage().GetData(); ModelLUT.exportIndexedLUTMin(lut, data, index); texture.Reload(true); } - private void updateImages(Texture texture, ModelImage image, TransferFunction tf, int index ) { + private void updateImages(Texture texture, ModelImage image, TransferFunction tf, int index) { final int lutHeight = texture.GetImage().GetBound(0); final byte[] data = texture.GetImage().GetData(); int offset = index * lutHeight * 4; @@ -1304,6 +1309,7 @@ private void updateImages(Texture texture, ModelImage image, TransferFunction tf } texture.Reload(true); } + /* * (non-Javadoc) * @@ -1327,8 +1333,7 @@ public boolean updateImages(ModelLUT LUTa, ModelLUT LUTb, boolean flag, int inte // } return false; } - - + @Override public void accurateModeChanged(boolean isAccurateMode) { accurateModeButton.setSelected(isAccurateMode); @@ -1435,7 +1440,7 @@ public boolean trackMIPAV_Python() throws Exception { // rightImage.wormData.getAnnotationsPath() ); ProcessBuilder processBuilder = new ProcessBuilder("python", baseFileDir + File.separator + "track_MIPAV.py", - WormData.getStraightAnnotationsPath(resultsDir(latticeFileDir, leftImage.wormImage)), + WormData.getStraightAnnotationsPath(resultsDir(latticeFileDir, leftImage.wormImage)), WormData.getStraightAnnotationsPath(resultsDir(latticeFileDir, rightImage.wormImage)), WormData.getIntegratedMarkerAnnotationsPath(resultsDir(latticeFileDir, leftImage.wormImage)), WormData.getIntegratedMarkerAnnotationsPath(resultsDir(latticeFileDir, rightImage.wormImage))); @@ -1463,10 +1468,10 @@ public boolean restraightenMIPAV_Python(IntegratedWormData data) throws Exceptio // leftImage.wormData.getAnnotationsPath() + " " + // rightImage.wormData.getAnnotationsPath() ); - String predictedTwisted = resultsDir(latticeFileDir, data.wormImage) + File.separator + "prediction" + File.separator - + "predicted_annotations.csv"; - String predictedStraight = resultsDir(latticeFileDir, data.wormImage) + File.separator + "prediction" + File.separator - + "predicted_straightened_annotations.csv"; + String predictedTwisted = resultsDir(latticeFileDir, data.wormImage) + File.separator + "prediction" + + File.separator + "predicted_annotations.csv"; + String predictedStraight = resultsDir(latticeFileDir, data.wormImage) + File.separator + "prediction" + + File.separator + "predicted_straightened_annotations.csv"; File file = new File(predictedTwisted); if (!file.exists()) @@ -1477,7 +1482,7 @@ public boolean restraightenMIPAV_Python(IntegratedWormData data) throws Exceptio ProcessBuilder processBuilder = new ProcessBuilder("python", baseFileDir[0] + File.separator + "restraighten_MIPAV.py", predictedStraight, predictedTwisted, - WormData.getIntegratedMarkerAnnotationsPath( resultsDir(latticeFileDir, rightImage.wormImage))); + WormData.getIntegratedMarkerAnnotationsPath(resultsDir(latticeFileDir, rightImage.wormImage))); processBuilder.redirectErrorStream(true); Process process = processBuilder.start(); @@ -1495,8 +1500,8 @@ public boolean restraightenMIPAV_Python(IntegratedWormData data) throws Exceptio private void runPython() { // delete the predicted files for the right image: - String fileName = resultsDir(latticeFileDir, rightImage.wormImage) + File.separator + "prediction" + File.separator - + "predicted_straightened_annotations.csv"; + String fileName = resultsDir(latticeFileDir, rightImage.wormImage) + File.separator + "prediction" + + File.separator + "predicted_straightened_annotations.csv"; File straightFile = new File(fileName); if (straightFile.exists()) { straightFile.delete(); @@ -1511,7 +1516,8 @@ private void runPython() { // when user presses 'predict' // prototype: // 1. straighten the right image - left annotations remain fixed: - ModelImage contourImage = rightImage.voiManager.untwistAnnotations(resultsDir(latticeFileDir, rightImage.wormImage), rightImage.contourImage); + ModelImage contourImage = rightImage.voiManager + .untwistAnnotations(resultsDir(latticeFileDir, rightImage.wormImage), rightImage.contourImage); if (rightImage.contourImage != null && rightImage.contourImage != contourImage) { rightImage.contourImage.disposeLocal(false); rightImage.contourImage = null; @@ -1563,7 +1569,7 @@ private VolumeImage[] untwistingTest() { activeImage.voiManager.setPaddingFactor(paddingFactor); ModelImage[] images = activeImage.voiManager.untwistTest(activeImage.hyperstack); VolumeImage[] hyperstack = new VolumeImage[images.length]; - for ( int i = 0; i < images.length; i++ ) { + for (int i = 0; i < images.length; i++) { images[i].unregisterAllVOIs(); hyperstack[i] = new VolumeImage(false, images[i], "" + i, null, 0, false); } @@ -1582,19 +1588,19 @@ private boolean checkGPUMemory(ModelImage[] images) { OpenCLInfo.getMaxMemSize(CL_DEVICE_TYPE_GPU, maxMemSizeArray); long maxAllocSize = maxMemSizeArray[0]; long totalMemSize = maxMemSizeArray[1]; - + long memoryUsed = 0; - for ( int i = 0; i < images.length; i++ ) { - if ( images[i] != null ) { + for (int i = 0; i < images.length; i++) { + if (images[i] != null) { int dataSize = images[i].getDataSize(); - if ( dataSize > (maxAllocSize / (Sizeof.cl_float)) ) { + if (dataSize > (maxAllocSize / (Sizeof.cl_float))) { return false; } memoryUsed += dataSize; } } - if ( memoryUsed >= (totalMemSize / Sizeof.cl_float) ) { + if (memoryUsed >= (totalMemSize / Sizeof.cl_float)) { return false; } return true; @@ -1643,12 +1649,12 @@ private boolean openHyperStack() { long memoryInUse = MipavUtil.getUsedHeapMemory(); for (int i = 0; i < includeRange.size(); i++) { - + ModelImage[] images = new ModelImage[baseFileDir.length]; String fileName = baseFileName + "_" + includeRange.elementAt(i) + ".tif"; File[] imageFiles = new File[baseFileDir.length]; - for ( int j = 0; j < baseFileDir.length; j++ ) { + for (int j = 0; j < baseFileDir.length; j++) { if (editMode == ReviewResults) { fileName = baseFileName + "_" + includeRange.elementAt(i) + "_straight.tif"; String subDirName = baseFileName + "_" + includeRange.elementAt(i) + File.separator; @@ -1656,16 +1662,15 @@ private boolean openHyperStack() { + File.separator; imageFiles[j] = new File(baseFileDir[j] + File.separator + subDirName + subDirNameResults + PlugInAlgorithmWormUntwistingJanelia.outputImages + File.separator + fileName); - } - else { + } else { imageFiles[j] = new File(baseFileDir[j] + File.separator + fileName); } if (imageFiles[j].exists()) { images[j] = openImage(imageFiles[j], fileName); - System.err.println("Opening... " + fileName + " " + images[j].isColorImage() ); + System.err.println("Opening... " + fileName + " " + images[j].isColorImage()); images[j].calcMinMax(); - if ( images[j].isColorImage() ) { + if (images[j].isColorImage()) { images[j] = convertToGray(images[j]); } } @@ -1673,12 +1678,12 @@ private boolean openHyperStack() { // Add memory check here: if (i == 0) { - if ( !checkGPUMemory(images) ) { + if (!checkGPUMemory(images)) { MipavUtil.displayError("Image size too big to load on GPU."); progressBar.setVisible(false); progressBar.dispose(); progressBar = null; - for ( int j = 0; j < images.length; j++ ) { + for (int j = 0; j < images.length; j++) { if (images[j] != null) { images[j].disposeLocal(); images[j] = null; @@ -1699,7 +1704,7 @@ private boolean openHyperStack() { progressBar.setVisible(false); progressBar.dispose(); progressBar = null; - for ( int j = 0; j < images.length; j++ ) { + for (int j = 0; j < images.length; j++) { if (images[j] != null) { images[j].disposeLocal(); images[j] = null; @@ -1713,7 +1718,7 @@ private boolean openHyperStack() { thresholdImage(images); leftImage.wormImage = images[0]; - + imageStack[i] = leftImage; System.err.println("... adding " + i + " " + leftImage.wormImage.getImageName()); @@ -1724,16 +1729,18 @@ private boolean openHyperStack() { leftImage.annotations = null; } leftImage.hyperstack = new VolumeImage[images.length]; - for ( int j = 0; j < images.length; j++ ) { + for (int j = 0; j < images.length; j++) { leftImage.hyperstack[j] = new VolumeImage(false, images[j], "" + j, null, 0, false); } - leftImage.volumeImage = leftImage.hyperstack[0]; //new VolumeImage(false, leftImage.wormImage, "A", null, 0, false); + leftImage.volumeImage = leftImage.hyperstack[0]; // new VolumeImage(false, leftImage.wormImage, "A", + // null, 0, false); leftImage.voiManager = new VOILatticeManagerInterface(null, leftImage.volumeImage.GetImage(), null, 0, true, null); leftImage.voiManager.addAccurateModeListener(this); - + byte[] aucData = new byte[256 * 4 * images.length]; - GraphicsImage cmImage = new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, 256, images.length, aucData, new String("colormap" + leftImage.volumeImage.GetImage().getImageFileName())); + GraphicsImage cmImage = new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, 256, images.length, + aucData, new String("colormap" + leftImage.volumeImage.GetImage().getImageFileName())); leftImage.colormap = new Texture(); leftImage.colormap.SetImage(cmImage); @@ -1753,7 +1760,7 @@ private boolean openHyperStack() { return false; success = true; - leftRenderer = new VolumeTriPlanarRender( leftImage.hyperstack, leftImage.colormap ); + leftRenderer = new VolumeTriPlanarRender(leftImage.hyperstack, leftImage.colormap); leftRenderer.setVisible(false); leftRenderer.addConfiguredListener(this); @@ -1853,10 +1860,10 @@ private boolean openHyperStack() { return success; } - private void thresholdImage(ModelImage[] images ) { + private void thresholdImage(ModelImage[] images) { if (thresholdImageCheck.isSelected()) { - for ( int i = 0; i < images.length; i++ ) { - if ( images[i] != null ) { + for (int i = 0; i < images.length; i++) { + if (images[i] != null) { for (int j = 0; j < images[i].getDataSize(); j++) { if (images[i].getFloat(j) > threshold) { @@ -1869,40 +1876,45 @@ private void thresholdImage(ModelImage[] images ) { } } - private ModelImage convertToGray(ModelImage image ) { + private ModelImage convertToGray(ModelImage image) { ModelImage result = new ModelImage(ModelStorageBase.FLOAT, image.getExtents(), JDialogBase.makeImageName(image.getImageFileName(), "")); - final int dimX = image.getExtents().length > 0 ? image.getExtents()[0] : 1; + final int dimX = image.getExtents().length > 0 ? image.getExtents()[0] : 1; final int dimY = image.getExtents().length > 1 ? image.getExtents()[1] : 1; - final int dimZ = image.getExtents().length > 2 ? image.getExtents()[2] : 1; - + final int dimZ = image.getExtents().length > 2 ? image.getExtents()[2] : 1; + float max = 0; float maxSum = 0; - for ( int z = 0; z < dimZ; z++ ) { - for ( int y = 0; y < dimY; y++ ) { - for ( int x = 0; x < dimX; x++ ) { + for (int z = 0; z < dimZ; z++) { + for (int y = 0; y < dimY; y++) { + for (int x = 0; x < dimX; x++) { float r = image.getFloatC(x, y, z, 1); float g = image.getFloatC(x, y, z, 2); float b = image.getFloatC(x, y, z, 3); - if ( r > max ) max = r; - if ( g > max ) max = g; - if ( b > max ) max = b; + if (r > max) + max = r; + if (g > max) + max = g; + if (b > max) + max = b; float sum = r + b + g; - if ( sum > maxSum ) maxSum = sum; + if (sum > maxSum) + maxSum = sum; result.set(x, y, z, sum); } } } - if ( maxSum > max ) { + if (maxSum > max) { float scale = maxSum / max; - for ( int i = 0; i < result.getDataSize(); i++ ) { - result.set(i, result.getFloat(i) / scale);; + for (int i = 0; i < result.getDataSize(); i++) { + result.set(i, result.getFloat(i) / scale); + ; } } result.calcMinMax(); return result; } - + private ModelImage combineImages(ModelImage imageA, ModelImage imageB, ModelImage imageC) { // imageA is never null and is always written into the 'green' channel... @@ -1912,7 +1924,7 @@ private ModelImage combineImages(ModelImage imageA, ModelImage imageB, ModelImag // Make algorithm ModelImage blank = null; - if ( imageB == null && imageC != null ) { + if (imageB == null && imageC != null) { blank = new ModelImage(ModelImage.SHORT, imageA.getExtents(), JDialogBase.makeImageName(imageA.getImageName(), "")); @@ -1920,8 +1932,7 @@ private ModelImage combineImages(ModelImage imageA, ModelImage imageB, ModelImag true, true); mathAlgo.run(); blank.disposeLocal(false); - } - else if ( imageB != null && imageC == null ) { + } else if (imageB != null && imageC == null) { blank = new ModelImage(ModelImage.SHORT, imageA.getExtents(), JDialogBase.makeImageName(imageA.getImageName(), "")); @@ -1929,8 +1940,7 @@ else if ( imageB != null && imageC == null ) { true, true); mathAlgo.run(); blank.disposeLocal(false); - } - else { + } else { AlgorithmRGBConcat mathAlgo = new AlgorithmRGBConcat(imageB, imageA, imageC, displayImage, true, false, 255, true, true); mathAlgo.run(); @@ -1966,7 +1976,7 @@ private void annotationAnimationFromSpreadSheet() { MipavUtil.centerOnScreen(progress); String inputDirName = baseFileDir[0] + File.separator; - System.err.println( inputDirName ); + System.err.println(inputDirName); final File inputFileDir = new File(inputDirName); Vector3f min = new Vector3f(Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE); @@ -2180,7 +2190,52 @@ private void annotationAnimationFromSpreadSheet() { progress.dispose(); progress = null; } - + + private JLabel plotLabel; + private JPanel plotPanel; + + // create a chart image from the values obtained via annotations + public static BufferedImage createChartImage(List values, String title) throws IOException { + XYSeries series = new XYSeries("Data"); + for (int i = 0; i < values.size() - 1; i++) { + series.add(i, values.get(i)); + } + XYSeriesCollection dataset = new XYSeriesCollection(); + dataset.addSeries(series); + + // create a line chart + JFreeChart chart = ChartFactory.createXYLineChart(title, "Sample Index", "Value", dataset, + PlotOrientation.VERTICAL, true, true, false); + + XYPlot plot = chart.getXYPlot(); + XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(); + renderer.setSeriesPaint(0, Color.BLUE); + renderer.setSeriesStroke(0, new BasicStroke(2.0f)); + plot.setRenderer(renderer); + + // create a buffered image to draw the chart + BufferedImage chartImage = new BufferedImage(400, 600, BufferedImage.TYPE_INT_RGB); + Graphics2D g2 = chartImage.createGraphics(); + chart.draw(g2, new java.awt.Rectangle(0, 0, 400, 600)); + g2.dispose(); + + return chartImage; + } + + // update the plot panel in responding to the clicking + public void updatePlotPanel(List values, String title) { + SwingUtilities.invokeLater(() -> { + try { + BufferedImage chartImage = createChartImage(values, title); + plotLabel.setIcon(new ImageIcon(chartImage)); + plotPanel.revalidate(); + plotPanel.repaint(); + } catch (IOException e) { + System.err.println("Error updating plot: " + e.getMessage()); + } + }); + } + /** * User-interface initialization. If the UI is integrated all panels are * displayed in one window. Otherwise the UI is divided into volume display and @@ -2272,42 +2327,48 @@ private void init() { dialogGUI.getContentPane().add(panel1, BorderLayout.NORTH); dialogGUI.getContentPane().add(panel2, BorderLayout.SOUTH); - lutPanel = new JPanel(new GridLayout(2,1)); + lutPanel = new JPanel(new GridLayout(2, 1)); lutTab = new JTabbedPane(); lutTab.addChangeListener(this); lutPanel.add(lutTab, BorderLayout.CENTER); - opacityPanel = new JPanel( new BorderLayout() ); + opacityPanel = new JPanel(new BorderLayout()); opacityTab = new JTabbedPane(); opacityTab.addChangeListener(this); opacityPanel.add(opacityTab, BorderLayout.CENTER); - clipPanel = new JPanel( new BorderLayout() ); - - //added a panel with button to be able to turn off accurate mode and switch to 3-color mode(not working currently) + clipPanel = new JPanel(new BorderLayout()); + + // added a panel with button to be able to turn off accurate mode and switch to + // 3-color mode(not working currently) accuratePanel = new JPanel(new BorderLayout()); - JPanel buttonPanel = new JPanel(); - //accurateModeButton = new JButton("Accurate Mode"); + JPanel buttonPanel = new JPanel(); + // accurateModeButton = new JButton("Accurate Mode"); accurateModeButton = new JToggleButton("Accurate Mode"); accurateModeChanged(true); accurateModeButton.setPreferredSize(new Dimension(200, 50)); - - // Add an action response to the button + // Add an action response to the button accurateModeButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - // activeImage.voiManager.toggleAccurateMode(); - - JToggleButton toggleButton = (JToggleButton) e.getSource(); - boolean isSelected = toggleButton.isSelected(); - activeImage.voiManager.toggleAccurateMode(); - } - }); - - // Add the accurateModeButton to the buttonPanel, then add the buttonPanel to the accuratePanel + @Override + public void actionPerformed(ActionEvent e) { + // activeImage.voiManager.toggleAccurateMode(); + + JToggleButton toggleButton = (JToggleButton) e.getSource(); + boolean isSelected = toggleButton.isSelected(); + activeImage.voiManager.toggleAccurateMode(); + } + }); + + // Add the accurateModeButton to the buttonPanel, then add the buttonPanel to + // the accuratePanel buttonPanel.add(accurateModeButton); accuratePanel.add(buttonPanel, BorderLayout.CENTER); - + plotPanel = new JPanel(new BorderLayout()); + plotPanel.setBorder(new TitledBorder("Plot")); + plotLabel = new JLabel(); + plotPanel.add(plotLabel, BorderLayout.CENTER); + accuratePanel.add(plotPanel, BorderLayout.SOUTH); + tabbedPane = new JTabbedPane(); tabbedPane.addTab("LUT", null, lutPanel); tabbedPane.addTab("Opacity", null, opacityPanel); @@ -2320,9 +2381,9 @@ public void actionPerformed(ActionEvent e) { displayControls.add(dialogGUI.getContentPane(), BorderLayout.NORTH); JPanel leftPanel = new JPanel(new BorderLayout()); leftPanel.add(displayControls, BorderLayout.NORTH); - + imageChannels = new JPanel(); - imageChannels.add(new JLabel("Select image channel:") ); + imageChannels.add(new JLabel("Select image channel:")); imageChannels.add(new JLabel("Select Mode:")); imageChannels.setVisible(false); leftPanel.add(imageChannels, BorderLayout.CENTER); @@ -2388,7 +2449,7 @@ private Container initGPUPanel(int editMode) { doneButton.setVisible(true); doneButton.setEnabled(true); backNextPanel.add(doneButton); - + JButton demo = gui.buildButton("demo"); demo.addActionListener(this); demo.setActionCommand("demo"); @@ -2397,7 +2458,7 @@ private Container initGPUPanel(int editMode) { // backNextPanel.add(demo); latticeSelectionPanel = new JPanel(); - + newLatticeButton = gui.buildButton("new lattice"); newLatticeButton.addActionListener(this); newLatticeButton.setActionCommand("newLattice"); @@ -2418,13 +2479,13 @@ private Container initGPUPanel(int editMode) { displayModel.setVisible(false); displayModel.setEnabled(true); latticeSelectionPanel.add(displayModel); - + displaySurface = gui.buildCheckBox("Show Surface", false); displaySurface.addActionListener(this); displaySurface.setActionCommand("displaySurface"); displaySurface.setVisible(false); displaySurface.setEnabled(true); - + editCrossSections = gui.buildCheckBox("Edit Sections", false); editCrossSections.addActionListener(this); editCrossSections.setActionCommand("editCrossSections"); @@ -2894,10 +2955,11 @@ private void openNeuriteCurves(IntegratedWormData data) { private void loadPredicted(IntegratedWormData data) { // load and save the straightened predicted values: - VOI markerAnnotations = LatticeModel.readAnnotationsCSV(resultsDir(latticeFileDir, data.wormImage) + File.separator - + "prediction" + File.separator + "predicted_straightened_annotations.csv"); + VOI markerAnnotations = LatticeModel.readAnnotationsCSV(resultsDir(latticeFileDir, data.wormImage) + + File.separator + "prediction" + File.separator + "predicted_straightened_annotations.csv"); if (markerAnnotations != null) { - LatticeModel.saveAnnotationsAsCSV( resultsDir(latticeFileDir, data.wormImage) + File.separator + "straightened_annotations", + LatticeModel.saveAnnotationsAsCSV( + resultsDir(latticeFileDir, data.wormImage) + File.separator + "straightened_annotations", "straightened_annotations.csv", markerAnnotations); } // load, save and display the predicted annotations: @@ -2913,7 +2975,8 @@ private void loadPredicted(IntegratedWormData data) { System.err.println(markerAnnotations + " " + markerAnnotations.getCurves().size()); data.annotations.add(markerAnnotations); data.voiManager.addAnnotations(data.annotations); - WormData.saveIntegratedMarkerAnnotations(resultsDir(latticeFileDir, data.wormImage), data.voiManager.getAnnotations()); + WormData.saveIntegratedMarkerAnnotations(resultsDir(latticeFileDir, data.wormImage), + data.voiManager.getAnnotations()); data.annotations = null; VOI annotations = data.voiManager.getAnnotations(); @@ -2941,8 +3004,7 @@ private void openAnnotations(IntegratedWormData data) { if (markers != null) { data.annotations.add(markers); } - } - else { + } else { // if (loadLegacyAnnotationsCheck.isSelected()) { // data.wormImage.setResolutions(originalResolutions); // } @@ -2970,7 +3032,7 @@ private void openAnnotations(IntegratedWormData data) { System.err.println("openAnnotations " + data.wormImage.getImageName() + " " + data.annotations.elementAt(0).getCurves().size()); } - + /** * Saves the seam cells to the default edited file for the current image. */ @@ -2981,8 +3043,9 @@ private void saveSeamCells() { if (imageIndex >= includeRange.size()) { return; } - WormData.saveSeamAnnotations(resultsDir(latticeFileDir, activeImage.wormImage), activeImage.voiManager.getAnnotations(), - (editMode != CheckSeam) && (editMode != IntegratedEditing), true); + WormData.saveSeamAnnotations(resultsDir(latticeFileDir, activeImage.wormImage), + activeImage.voiManager.getAnnotations(), (editMode != CheckSeam) && (editMode != IntegratedEditing), + true); } /** @@ -3031,7 +3094,7 @@ private boolean setVariables() { } baseFileName = baseFileNameText.getText(); - + includeRange = new Vector(); String rangeFusion = rangeFusionText.getText(); if (rangeFusion != null) { @@ -3077,36 +3140,38 @@ private boolean setVariables() { } private boolean imageChannelsInit = false; + private void initImageChannels(IntegratedWormData integratedData) { - if ( imageChannelsInit ) return; + if (imageChannelsInit) + return; imageChannelsInit = true; int numImages = integratedData.hyperstack.length; String[] subDir = new String[numImages]; - for ( int i = 0; i < numImages; i++ ) { + for (int i = 0; i < numImages; i++) { int index = baseFileDir[i].lastIndexOf(File.separator) + 1; int len = baseFileDir[i].length(); - subDir[i] = baseFileDir[i].substring(index,len); + subDir[i] = baseFileDir[i].substring(index, len); JCheckBox box = new JCheckBox(subDir[i], true); box.setActionCommand(subDir[i]); box.addActionListener(this); - imageChannels.add( box ); + imageChannels.add(box); } } - + private int imageChannel(String cmd) { int numImages = activeImage.hyperstack.length; - for ( int i = 0; i < numImages; i++ ) { + for (int i = 0; i < numImages; i++) { int index = baseFileDir[i].lastIndexOf(File.separator) + 1; int len = baseFileDir[i].length(); - String subDir = baseFileDir[i].substring(index,len); - if ( subDir.equals(cmd) ) { + String subDir = baseFileDir[i].substring(index, len); + if (subDir.equals(cmd)) { return i; } } return -1; } - + /** * Creates or updates the histogram / LUT panel and opacity panels when a new * image is loaded. @@ -3167,9 +3232,9 @@ private void initHistoLUTPanel(IntegratedWormData integratedData) { int numImages = integratedData.hyperstack.length; integratedData.volOpacityPanel = new JPanelVolumeOpacity[numImages]; - for ( int i = 0; i < numImages; i++ ) { - integratedData.volOpacityPanel[i] = new JPanelVolumeOpacity(integratedData.hyperstack[i].GetImage(), - null, null, null, true); + for (int i = 0; i < numImages; i++) { + integratedData.volOpacityPanel[i] = new JPanelVolumeOpacity(integratedData.hyperstack[i].GetImage(), null, + null, null, true); integratedData.volOpacityPanel[i].addPropertyChangeListener(this); TransferFunction kTransfer = integratedData.volOpacityPanel[i].getCompA().getOpacityTransferFunction(); @@ -3179,18 +3244,17 @@ private void initHistoLUTPanel(IntegratedWormData integratedData) { // System.err.println( "initHistoLUTPanel " + // integratedData.volumeImage.getLUT() ); integratedData.lutHistogramPanel = new JFrameHistogram[numImages]; - for ( int i = 0; i < numImages; i++ ) { + for (int i = 0; i < numImages; i++) { - integratedData.lutHistogramPanel[i] = new JFrameHistogram(this, integratedData.hyperstack[i].GetImage(), null, - integratedData.hyperstack[i].getLUT(), null); - integratedData.lutHistogramPanel[i].histogramLUT(true, false, false);//!integratedData.volumeImage.GetImage().isColorImage()); + integratedData.lutHistogramPanel[i] = new JFrameHistogram(this, integratedData.hyperstack[i].GetImage(), + null, integratedData.hyperstack[i].getLUT(), null); + integratedData.lutHistogramPanel[i].histogramLUT(true, false, false);// !integratedData.volumeImage.GetImage().isColorImage()); } - // if (integratedData.volumeImage.GetImage().isColorImage()) { // integratedData.displayBothChannels.setSelected(true); // } - for ( int i = 0; i < numImages; i++ ) { + for (int i = 0; i < numImages; i++) { integratedData.hyperstack[i].GetImage().addImageDisplayListener(this); } } @@ -3201,13 +3265,13 @@ private void updateHistoLUTPanels(IntegratedWormData integratedData) { // integratedData.lutHistogramPanel.getContainingPanel() ); int numImages = integratedData.hyperstack.length; String[] subDir = new String[numImages]; - if ( !integratedData.colorMapInit ) { + if (!integratedData.colorMapInit) { integratedData.colorMapInit = true; - for ( int i = 0; i < numImages; i++ ) { + for (int i = 0; i < numImages; i++) { int index = baseFileDir[i].lastIndexOf(File.separator) + 1; int len = baseFileDir[i].length(); - subDir[i] = baseFileDir[i].substring(index,len); - if ( subDir[i].equals("405") ) { + subDir[i] = baseFileDir[i].substring(index, len); + if (subDir[i].equals("405")) { // set transfer function: ModelLUT lut = integratedData.hyperstack[i].GetLUT(); lut.makeBlueTransferFunctions(); @@ -3216,7 +3280,7 @@ private void updateHistoLUTPanels(IntegratedWormData integratedData) { tf.replacePoint(64, 0, 2); lut.makeLUT(256); } - if ( subDir[i].equals("488") ) { + if (subDir[i].equals("488")) { // set transfer function: ModelLUT lut = integratedData.hyperstack[i].GetLUT(); lut.makeGreenTransferFunctions(); @@ -3225,7 +3289,7 @@ private void updateHistoLUTPanels(IntegratedWormData integratedData) { tf.replacePoint(64, 0, 2); lut.makeLUT(256); } - if ( subDir[i].equals("561") ) { + if (subDir[i].equals("561")) { // set transfer function: ModelLUT lut = integratedData.hyperstack[i].GetLUT(); lut.makeRedTransferFunctions(); @@ -3234,7 +3298,7 @@ private void updateHistoLUTPanels(IntegratedWormData integratedData) { tf.replacePoint(64, 0, 2); lut.makeLUT(256); } - if ( subDir[i].equals("637") ) { + if (subDir[i].equals("637")) { // set transfer function: ModelLUT lut = integratedData.hyperstack[i].GetLUT(); TransferFunction tf = lut.getTransferFunction(); @@ -3244,7 +3308,7 @@ private void updateHistoLUTPanels(IntegratedWormData integratedData) { } } } - + // re-add lutTab to lutPanel due to removal in enableNext if (lutPanel.getComponentCount() == 0) { lutPanel.add(lutTab, BorderLayout.CENTER); @@ -3254,52 +3318,52 @@ private void updateHistoLUTPanels(IntegratedWormData integratedData) { opacityPanel.add(opacityTab, BorderLayout.CENTER); } - opacityTab.removeAll(); - for ( int i = 0; i < numImages; i++ ) { + opacityTab.removeAll(); + for (int i = 0; i < numImages; i++) { int index = baseFileDir[i].lastIndexOf(File.separator) + 1; int len = baseFileDir[i].length(); - subDir[i] = baseFileDir[i].substring(index,len); - - opacityTab.addTab( subDir[i] + File.separator + integratedData.hyperstack[i].GetImage().getImageName(), - null, integratedData.volOpacityPanel[i].getMainPanel() ); + subDir[i] = baseFileDir[i].substring(index, len); + + opacityTab.addTab(subDir[i] + File.separator + integratedData.hyperstack[i].GetImage().getImageName(), null, + integratedData.volOpacityPanel[i].getMainPanel()); - - final TransferFunction kTransfer = integratedData.volOpacityPanel[i].getCompA().getOpacityTransferFunction(); + final TransferFunction kTransfer = integratedData.volOpacityPanel[i].getCompA() + .getOpacityTransferFunction(); updateImages(integratedData.colormap, integratedData.hyperstack[i].GetImage(), kTransfer, i); integratedData.volOpacityPanel[i].getCompA().showHistogram(); integratedData.hyperstack[i].UpdateImages(kTransfer, 0, null); - if ( integratedData.previewHS != null ) { + if (integratedData.previewHS != null) { integratedData.previewHS[i].UpdateImages(kTransfer, 0, null); } } if (tabbedPane.getSelectedComponent() == opacityPanel) { int which = opacityTab.getSelectedIndex(); - if ( which != -1 ) { + if (which != -1) { integratedData.volOpacityPanel[which].getCompA().showHistogram(); } } lutTab.removeAll(); - for ( int i = 0; i < numImages; i++ ) { - lutTab.addTab( subDir[i] + File.separator + integratedData.hyperstack[i].GetImage().getImageName(), - null, integratedData.lutHistogramPanel[i].getContainingPanel()); - updateImages(integratedData.colormap, integratedData.hyperstack[i].GetLUT(), i ); - + for (int i = 0; i < numImages; i++) { + lutTab.addTab(subDir[i] + File.separator + integratedData.hyperstack[i].GetImage().getImageName(), null, + integratedData.lutHistogramPanel[i].getContainingPanel()); + updateImages(integratedData.colormap, integratedData.hyperstack[i].GetLUT(), i); + integratedData.hyperstack[i].UpdateImages(integratedData.hyperstack[i].getLUT()); - if ( integratedData.previewHS != null ) { + if (integratedData.previewHS != null) { integratedData.previewHS[i].UpdateImages(integratedData.hyperstack[i].getLUT()); } integratedData.lutHistogramPanel[i].redrawFrames(); } if (tabbedPane.getSelectedComponent() == lutPanel) { - int which = lutTab.getSelectedIndex(); - if ( which != -1 ) { + int which = lutTab.getSelectedIndex(); + if (which != -1) { integratedData.lutHistogramPanel[which].redrawFrames(); } } - + lutPanel.revalidate(); } @@ -3336,10 +3400,10 @@ private void updateSurfacePanels() { // tabbedPane.addTab("Surface", null, lightsPanel); // } } - - private void updateSelectionPanel() { - // accurateModeChanged(activeImage.voiManager.isAccurateMode()); - + + private void updateSelectionPanel() { + // accurateModeChanged(activeImage.voiManager.isAccurateMode()); + VOILatticeManagerInterface voiManager = activeImage.voiManager; voiManager.setAccurateMode(voiManager.isAccurateMode()); } @@ -3389,7 +3453,7 @@ public void stateChanged(ChangeEvent arg0) { } private void saveIntegrated() { - activeImage.voiManager.setSharedDirectory( resultsDir(latticeFileDir, activeImage.wormImage)); + activeImage.voiManager.setSharedDirectory(resultsDir(latticeFileDir, activeImage.wormImage)); if (editMode == EditSeamCells || editMode == CheckSeam) { saveSeamCells(); @@ -3405,76 +3469,78 @@ private void saveIntegrated() { // separately so not untwisted twice: saveSplineCurves(); - if ( activeImage.voiManager != null ) { + if (activeImage.voiManager != null) { // save annotations not in splines: - WormData.saveIntegratedMarkerAnnotations(resultsDir(latticeFileDir, activeImage.wormImage), activeImage.voiManager.getAnnotations()); + WormData.saveIntegratedMarkerAnnotations(resultsDir(latticeFileDir, activeImage.wormImage), + activeImage.voiManager.getAnnotations()); } } - + private void setDefaultInputList(String dir) { File file = new File(dir); - if ( file.exists() && file.isDirectory() ) { + if (file.exists() && file.isDirectory()) { final String[] list = file.list(); String imageList = ""; for (int i = 0; i < list.length; i++) { - if ( list[i].endsWith(".tif") ) { - String temp = list[i].substring(list[i].lastIndexOf("_") + 1, list[i].indexOf(".tif") ); + if (list[i].endsWith(".tif")) { + String temp = list[i].substring(list[i].lastIndexOf("_") + 1, list[i].indexOf(".tif")); System.err.println(list[i] + " " + temp); - if ( imageList != "" ) imageList += ","; + if (imageList != "") + imageList += ","; imageList += temp; } } - if ( imageList != "" ) { + if (imageList != "") { rangeFusionText.setText(imageList); } } } - + private class UntwistDialog extends JDialogBase implements ActionListener { private JCheckBox[] volumeChecks; private String[] volumeDirs; private JTextField latticeOutputDir; - public UntwistDialog( String baseDir ) { + + public UntwistDialog(String baseDir) { File file = new File(baseDir); - if ( file.exists() && file.isDirectory() ) { + if (file.exists() && file.isDirectory()) { final String[] list = file.list(); Vector tempList = new Vector(); for (int i = 0; i < list.length; i++) { File subDir = new File(file.getAbsolutePath() + File.separator + list[i]); - if ( subDir.exists() && subDir.isDirectory() ) - { + if (subDir.exists() && subDir.isDirectory()) { System.err.println(file.getAbsolutePath() + File.separator + list[i]); tempList.add(list[i]); } } - if ( tempList.size() > 0 ) { + if (tempList.size() > 0) { init(baseDir, tempList); setVisible(true); } } - } - - // Diyi: Created a simpler version of actionPerformed to replace the previous one + } + + // Diyi: Created a simpler version of actionPerformed to replace the previous + // one public void actionPerformed(ActionEvent event) { String command = event.getActionCommand(); Object source = event.getSource(); System.err.println("UntwistDialog " + command); - if ( command.equals("Cancel") ) { + if (command.equals("Cancel")) { baseFileLocText.setText(""); latticeOutputDir = null; setVisible(false); - } - else if ( command.equals("OK") ) { - + } else if (command.equals("OK")) { + ArrayList list = new ArrayList<>(volumeChecks.length); - - for (int i = 0; i < volumeChecks.length; i++){ + + for (int i = 0; i < volumeChecks.length; i++) { if (volumeChecks[i].isSelected()) { - list.add(volumeDirs[i]); + list.add(volumeDirs[i]); } - baseFileDir = new String [list.size()]; + baseFileDir = new String[list.size()]; list.toArray(baseFileDir); } if (list.size() == 0) @@ -3482,9 +3548,8 @@ else if ( command.equals("OK") ) { setVisible(false); } } - - - private void init( String baseDir, Vector tempList ) { + + private void init(String baseDir, Vector tempList) { setForeground(Color.black); setTitle("Untwisting C.elegans - lattice - 2.0"); @@ -3493,57 +3558,58 @@ private void init( String baseDir, Vector tempList ) { } catch (FileNotFoundException e) { Preferences.debug("Failed to load default icon", Preferences.DEBUG_MINOR); } - JPanel input = new JPanel( new GridLayout(tempList.size() + 1, 1)); + JPanel input = new JPanel(new GridLayout(tempList.size() + 1, 1)); GuiBuilder gui = new GuiBuilder(this); volumeChecks = new JCheckBox[tempList.size()]; volumeDirs = new String[tempList.size()]; String latticeDir = null; - for ( int i = 0; i < tempList.size(); i++ ) { + for (int i = 0; i < tempList.size(); i++) { volumeChecks[i] = gui.buildCheckBox(tempList.elementAt(i), true); volumeChecks[i].addActionListener(this); input.add(volumeChecks[i].getParent()); volumeDirs[i] = new String(baseDir + File.separator + tempList.elementAt(i)); final File subDir = new File(volumeDirs[i]); - if ( latticeDir == null ) { - latticeDir = new String(baseDir + File.separator + tempList.elementAt(i)); + if (latticeDir == null) { + latticeDir = new String(baseDir + File.separator + tempList.elementAt(i)); + } + if (containsLattice(subDir)) { + latticeDir = new String(baseDir + File.separator + tempList.elementAt(i)); } - if ( containsLattice(subDir) ) { - latticeDir = new String(baseDir + File.separator + tempList.elementAt(i)); - } - + } - - latticeOutputDir = gui.buildFileField("Lattice Directory:", latticeDir, false, JFileChooser.DIRECTORIES_ONLY, - this); + + latticeOutputDir = gui.buildFileField("Lattice Directory:", latticeDir, false, + JFileChooser.DIRECTORIES_ONLY, this); input.add(latticeOutputDir.getParent()); - + System.err.println(latticeDir); getContentPane().add(input, BorderLayout.CENTER); - + JPanel buttonPanel = new JPanel(); - buttonPanel.add(JDialogBase.buildOKButton( "OK", this )); - buttonPanel.add(JDialogBase.buildCancelButton( "Cancel", this )); + buttonPanel.add(JDialogBase.buildOKButton("OK", this)); + buttonPanel.add(JDialogBase.buildCancelButton("Cancel", this)); getContentPane().add(buttonPanel, BorderLayout.SOUTH); setModal(true); - if ( bar != null ) bar.setVisible(false); + if (bar != null) + bar.setVisible(false); pack(); } - - private boolean containsLattice(File dir ) { - if ( dir.exists() && dir.isDirectory() ) { + private boolean containsLattice(File dir) { + + if (dir.exists() && dir.isDirectory()) { final String[] list = dir.list(); - for ( int i = 0; i < list.length; i++ ) { - if ( list[i].equals("lattice_final") ) { + for (int i = 0; i < list.length; i++) { + if (list[i].equals("lattice_final")) { return true; - } - else { - File file = new File(dir.getAbsolutePath() + File.separator + list[i] ); - if ( file.exists() && file.isDirectory() ) { - if ( containsLattice(file) ) return true; + } else { + File file = new File(dir.getAbsolutePath() + File.separator + list[i]); + if (file.exists() && file.isDirectory()) { + if (containsLattice(file)) + return true; } } } @@ -3551,76 +3617,79 @@ private boolean containsLattice(File dir ) { return false; } } - + public void setDemoValues() { - //baseFileLocText.setText("\\\\nearline4.hhmi.org\\shroff\\shrofflab\\efn-1\\Tracking\\Pos0\\For_Tracking"); + // baseFileLocText.setText("\\\\nearline4.hhmi.org\\shroff\\shrofflab\\efn-1\\Tracking\\Pos0\\For_Tracking"); // X:\shrofflab\RW10752_NU\Untwisting\031219_RW10752_NU\RW10752_NU\RW10752_NU\Pos2\Decon_registered - - //diyi local test path: E:\Diyi\Pos2\Decon_registered - //String access_path = "E:\\Diyi\\Pos2\\Decon_registered"; - - //from diyi error mimic path: X:\shrofflab\Vab-1\Tracking\Pos0\SPIMB\Reg_Sample\For_Tracking\RegB - //from jhonny:Z:\shrofflab\Vab-1\Tracking\Pos0\SPIMB\Reg_Sample\For_Tracking\RegB\Decon_reg_14\Decon_reg_14_results - //copied to diyi from jhonny: E:\Diyi\SPIMB\Reg_Sample\For_Tracking\RegB\Decon_reg_14\Decon_reg_14_results - - //from online:\\\\nearline4.hhmi.org\\shroff\\shrofflab\\efn-1\\Tracking\\Pos0\\For_Tracking - //String access_path = "\\\\nearline4.hhmi.org\\shroff\\shrofflab\\efn-1\\Tracking\\Pos0\\For_Tracking"; - - //from online jhonny: X:\shrofflab\Vab-1\Tracking\Pos0\SPIMB\Reg_Sample\For_Tracking\RegB\Decon_reg_14\Decon_reg_14_results - //String access_path = "X:\\shrofflab\\Vab-1\\Tracking\\Pos0\\SPIMB\\Reg_Sample\\For_Tracking"; - - //String access_path = "E:\\Diyi\\SPIMB\\Reg_Sample\\For_Tracking"; + + // diyi local test path: E:\Diyi\Pos2\Decon_registered + // String access_path = "E:\\Diyi\\Pos2\\Decon_registered"; + + // from diyi error mimic path: + // X:\shrofflab\Vab-1\Tracking\Pos0\SPIMB\Reg_Sample\For_Tracking\RegB + // from + // jhonny:Z:\shrofflab\Vab-1\Tracking\Pos0\SPIMB\Reg_Sample\For_Tracking\RegB\Decon_reg_14\Decon_reg_14_results + // copied to diyi from jhonny: + // E:\Diyi\SPIMB\Reg_Sample\For_Tracking\RegB\Decon_reg_14\Decon_reg_14_results + + // from + // online:\\\\nearline4.hhmi.org\\shroff\\shrofflab\\efn-1\\Tracking\\Pos0\\For_Tracking + // String access_path = + // "\\\\nearline4.hhmi.org\\shroff\\shrofflab\\efn-1\\Tracking\\Pos0\\For_Tracking"; + + // from online jhonny: + // X:\shrofflab\Vab-1\Tracking\Pos0\SPIMB\Reg_Sample\For_Tracking\RegB\Decon_reg_14\Decon_reg_14_results + // String access_path = + // "X:\\shrofflab\\Vab-1\\Tracking\\Pos0\\SPIMB\\Reg_Sample\\For_Tracking"; + + // String access_path = "E:\\Diyi\\SPIMB\\Reg_Sample\\For_Tracking"; String access_path = "D:\\shroff\\For_Tracking"; baseFileLocText.setText(access_path); editLattice.setSelected(true); - //actionPerformed(new ActionEvent(this, 0, "BrowseConclude")); - + // actionPerformed(new ActionEvent(this, 0, "BrowseConclude")); + String[] volumeDirs; - + String baseDir = baseFileLocText.getText(); File file = new File(baseDir); - + final String[] list = file.list(); Vector tempList = new Vector(); for (int i = 0; i < list.length; i++) { File subDir = new File(file.getAbsolutePath() + File.separator + list[i]); - if ( subDir.exists() && subDir.isDirectory() ) - { + if (subDir.exists() && subDir.isDirectory()) { System.err.println(file.getAbsolutePath() + File.separator + list[i]); - if (list[i].equals("RegA") || list[i].equals("RegB")) - { + if (list[i].equals("RegA") || list[i].equals("RegB")) { tempList.add(list[i]); } } } - + volumeDirs = new String[tempList.size()]; - + int count = 0; - for ( int i = 0; i < volumeDirs.length; i++ ) { - count++; + for (int i = 0; i < volumeDirs.length; i++) { + count++; volumeDirs[i] = new String(baseDir + File.separator + tempList.elementAt(i)); } - if ( count == 0 ) { + if (count == 0) { baseFileLocText.setText(""); - //latticeOutputDir = null; - } - else { + // latticeOutputDir = null; + } else { baseFileDir = new String[count]; count = 0; - for ( int i = 0; i < volumeDirs.length; i++ ) { + for (int i = 0; i < volumeDirs.length; i++) { baseFileDir[count++] = volumeDirs[i]; } } - //baseFileLocText.setText("\\\\nearline4.hhmi.org\\shroff\\shrofflab\\efn-1\\Tracking\\Pos0\\For_Tracking\\RegB"); + // baseFileLocText.setText("\\\\nearline4.hhmi.org\\shroff\\shrofflab\\efn-1\\Tracking\\Pos0\\For_Tracking\\RegB"); latticeFileDir = new String(access_path + "\\RegB"); setDefaultInputList(latticeFileDir); rangeFusionText.setText("60-61"); - + actionPerformed(new ActionEvent(this, 0, "start")); } } - From 2d62e123ff640d255f517b9c01876a3aa622699f Mon Sep 17 00:00:00 2001 From: chend Date: Wed, 22 May 2024 10:51:05 -0400 Subject: [PATCH 14/39] Add a valueMarker for highlight the maximum value in the plot --- .../PlugInDialogVolumeRenderDualJanelia.java | 69 ++++++++++++------- 1 file changed, 44 insertions(+), 25 deletions(-) diff --git a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java index 7e56055a31..f927e50cf7 100644 --- a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java +++ b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java @@ -124,10 +124,13 @@ import org.jfree.chart.ChartFactory; import org.jfree.chart.JFreeChart; import org.jfree.chart.plot.PlotOrientation; +import org.jfree.chart.plot.ValueMarker; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; +import org.jfree.ui.RectangleAnchor; +import org.jfree.ui.TextAnchor; import org.jocl.Sizeof; import WildMagic.LibFoundation.Mathematics.Mathf; @@ -2196,31 +2199,47 @@ private void annotationAnimationFromSpreadSheet() { // create a chart image from the values obtained via annotations public static BufferedImage createChartImage(List values, String title) throws IOException { - XYSeries series = new XYSeries("Data"); - for (int i = 0; i < values.size() - 1; i++) { - series.add(i, values.get(i)); - } - XYSeriesCollection dataset = new XYSeriesCollection(); - dataset.addSeries(series); - - // create a line chart - JFreeChart chart = ChartFactory.createXYLineChart(title, "Sample Index", "Value", dataset, - PlotOrientation.VERTICAL, true, true, false); - - XYPlot plot = chart.getXYPlot(); - XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(); - renderer.setSeriesPaint(0, Color.BLUE); - renderer.setSeriesStroke(0, new BasicStroke(2.0f)); - plot.setRenderer(renderer); - - // create a buffered image to draw the chart - BufferedImage chartImage = new BufferedImage(400, 600, BufferedImage.TYPE_INT_RGB); - Graphics2D g2 = chartImage.createGraphics(); - chart.draw(g2, new java.awt.Rectangle(0, 0, 400, 600)); - g2.dispose(); - - return chartImage; - } + XYSeries series = new XYSeries("Data"); + float maxValue = -Float.MAX_VALUE; + int maxIndex = -1; + + for (int i = 0; i < values.size(); i++) { + float value = values.get(i); + series.add(i, value); + if (value > maxValue) { + maxValue = value; + maxIndex = i; + } + } + + XYSeriesCollection dataset = new XYSeriesCollection(); + dataset.addSeries(series); + + JFreeChart chart = ChartFactory.createXYLineChart( + title, "Index", "Value", dataset, + PlotOrientation.VERTICAL, true, true, false); + + XYPlot plot = chart.getXYPlot(); + XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(); + renderer.setSeriesPaint(0, Color.BLUE); + renderer.setSeriesStroke(0, new BasicStroke(2.0f)); + plot.setRenderer(renderer); + + // Add a marker to show the maximum value + ValueMarker marker = new ValueMarker(maxIndex); + marker.setPaint(Color.RED); + marker.setLabel("Max Value: " + maxValue); + marker.setLabelAnchor(RectangleAnchor.CENTER); + marker.setLabelTextAnchor(TextAnchor.CENTER_LEFT); + plot.addDomainMarker(marker); + + BufferedImage chartImage = new BufferedImage(400, 600, BufferedImage.TYPE_INT_RGB); + Graphics2D g2 = chartImage.createGraphics(); + chart.draw(g2, new java.awt.Rectangle(0, 0, 400, 600)); + g2.dispose(); + + return chartImage; + } // update the plot panel in responding to the clicking public void updatePlotPanel(List values, String title) { From ede406b403f0957098b9e6e582d5b5bed2248eb8 Mon Sep 17 00:00:00 2001 From: chend Date: Thu, 23 May 2024 16:02:49 -0400 Subject: [PATCH 15/39] Added the ValueMarker with mouseDragged function --- .../PlugInDialogVolumeRenderDualJanelia.java | 61 +++++++++++++++---- 1 file changed, 49 insertions(+), 12 deletions(-) diff --git a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java index f927e50cf7..e9ef13e748 100644 --- a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java +++ b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java @@ -78,9 +78,13 @@ import java.awt.GridBagLayout; import java.awt.GridLayout; import java.awt.Insets; +import java.awt.Point; +import java.awt.Rectangle; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.awt.image.BufferedImage; @@ -94,6 +98,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Vector; import java.util.stream.Collectors; @@ -129,8 +134,9 @@ import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; -import org.jfree.ui.RectangleAnchor; -import org.jfree.ui.TextAnchor; +import org.jfree.chart.ui.Layer; +import org.jfree.chart.ui.RectangleAnchor; +import org.jfree.chart.ui.TextAnchor; import org.jocl.Sizeof; import WildMagic.LibFoundation.Mathematics.Mathf; @@ -2198,7 +2204,7 @@ private void annotationAnimationFromSpreadSheet() { private JPanel plotPanel; // create a chart image from the values obtained via annotations - public static BufferedImage createChartImage(List values, String title) throws IOException { + public static JFreeChart createChart(List values, String title) throws IOException { XYSeries series = new XYSeries("Data"); float maxValue = -Float.MAX_VALUE; int maxIndex = -1; @@ -2232,20 +2238,24 @@ public static BufferedImage createChartImage(List values, String title) t marker.setLabelAnchor(RectangleAnchor.CENTER); marker.setLabelTextAnchor(TextAnchor.CENTER_LEFT); plot.addDomainMarker(marker); - - BufferedImage chartImage = new BufferedImage(400, 600, BufferedImage.TYPE_INT_RGB); - Graphics2D g2 = chartImage.createGraphics(); - chart.draw(g2, new java.awt.Rectangle(0, 0, 400, 600)); - g2.dispose(); - - return chartImage; + + + return chart; } + + JFreeChart selectionChart; // update the plot panel in responding to the clicking - public void updatePlotPanel(List values, String title) { + public void updatePlotPanel(List values, String title) { SwingUtilities.invokeLater(() -> { try { - BufferedImage chartImage = createChartImage(values, title); + selectionChart = createChart(values, title); + + BufferedImage chartImage = new BufferedImage(400, 600, BufferedImage.TYPE_INT_RGB); + Graphics2D g2 = chartImage.createGraphics(); + selectionChart.draw(g2, new Rectangle(0, 0, 400, 600)); + g2.dispose(); + plotLabel.setIcon(new ImageIcon(chartImage)); plotPanel.revalidate(); plotPanel.repaint(); @@ -2255,6 +2265,7 @@ public void updatePlotPanel(List values, String title) { }); } + /** * User-interface initialization. If the UI is integrated all panels are * displayed in one window. Otherwise the UI is divided into volume display and @@ -2385,6 +2396,32 @@ public void actionPerformed(ActionEvent e) { plotPanel = new JPanel(new BorderLayout()); plotPanel.setBorder(new TitledBorder("Plot")); plotLabel = new JLabel(); + plotLabel.addMouseMotionListener(new MouseAdapter() { + public void mouseDragged(MouseEvent e) { + Point p = e.getPoint(); + System.out.println(p); + + Rectangle plotArea = new Rectangle(0, 0, 400, 600); + XYPlot plot = (XYPlot) selectionChart.getPlot(); + Collection dm = plot.getDomainMarkers(Layer.FOREGROUND); + ValueMarker firstDM = (ValueMarker) dm.toArray()[0]; + double xValue = plot.getDomainAxis().java2DToValue(p.getX(), plotArea, plot.getDomainAxisEdge()); + firstDM.setValue(xValue); + + XYSeries series = ((XYSeriesCollection) plot.getDataset()).getSeries(0); + double yValue = series.getY((int) xValue).doubleValue(); + firstDM.setLabel("Value: " + yValue); + + BufferedImage chartImage = new BufferedImage(400, 600, BufferedImage.TYPE_INT_RGB); + Graphics2D g2 = chartImage.createGraphics(); + selectionChart.draw(g2, new Rectangle(0, 0, 400, 600)); + g2.dispose(); + + plotLabel.setIcon(new ImageIcon(chartImage)); + plotPanel.revalidate(); + plotPanel.repaint(); + } + }); plotPanel.add(plotLabel, BorderLayout.CENTER); accuratePanel.add(plotPanel, BorderLayout.SOUTH); From 92b79ef2d535c294ab5f0408cba72133b1f634d7 Mon Sep 17 00:00:00 2001 From: chend Date: Fri, 24 May 2024 11:51:26 -0400 Subject: [PATCH 16/39] removed the EXIT_ON_CLOSE line --- src/org/janelia/mipav/BatchProcessLogFrame.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/org/janelia/mipav/BatchProcessLogFrame.java b/src/org/janelia/mipav/BatchProcessLogFrame.java index 5f6ac23e0c..cb0af8ef5e 100644 --- a/src/org/janelia/mipav/BatchProcessLogFrame.java +++ b/src/org/janelia/mipav/BatchProcessLogFrame.java @@ -44,6 +44,7 @@ public BatchProcessLogFrame() { // write code here this.frame = new JFrame("Batch Process Log"); this.layout = new FlowLayout(); + //this.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); createGUI(); } From bf351750fc6ab5bf58c2e73ade0eb046d73cfac7 Mon Sep 17 00:00:00 2001 From: chend Date: Fri, 24 May 2024 11:52:25 -0400 Subject: [PATCH 17/39] updated removed all the EXIT_ON_CLOSE --- src/org/janelia/mipav/BatchProcessLogFrame.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/org/janelia/mipav/BatchProcessLogFrame.java b/src/org/janelia/mipav/BatchProcessLogFrame.java index cb0af8ef5e..2e891898ee 100644 --- a/src/org/janelia/mipav/BatchProcessLogFrame.java +++ b/src/org/janelia/mipav/BatchProcessLogFrame.java @@ -44,7 +44,6 @@ public BatchProcessLogFrame() { // write code here this.frame = new JFrame("Batch Process Log"); this.layout = new FlowLayout(); - //this.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); createGUI(); } @@ -132,7 +131,6 @@ private void createGUI() { @Override public void actionPerformed(ActionEvent e) { - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); String data = textArea.getText().trim(); // read contents of text area into 'data' if (!data.equals("")) { From c69c82a10c0a61978e3b3a0a971862d7bd4f141a Mon Sep 17 00:00:00 2001 From: chend Date: Tue, 28 May 2024 17:08:08 -0400 Subject: [PATCH 18/39] post code review, removed unused variables, comments, and added a dialog for failed file save. --- .../janelia/mipav/BatchProcessLogFrame.java | 73 +++++++------------ 1 file changed, 26 insertions(+), 47 deletions(-) diff --git a/src/org/janelia/mipav/BatchProcessLogFrame.java b/src/org/janelia/mipav/BatchProcessLogFrame.java index 2e891898ee..c19b28abda 100644 --- a/src/org/janelia/mipav/BatchProcessLogFrame.java +++ b/src/org/janelia/mipav/BatchProcessLogFrame.java @@ -1,77 +1,53 @@ package org.janelia.mipav; -import java.awt.FlowLayout; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; -import java.awt.LayoutManager; import java.awt.Toolkit; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.StringSelection; import java.awt.Dialog.ModalityType; -import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; +import java.io.FileNotFoundException; import java.io.FileWriter; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JRadioButton; import javax.swing.JScrollPane; import javax.swing.JTextArea; -import javax.swing.JTextField; public class BatchProcessLogFrame implements ActionListener { - // add variables here + // variables JFrame frame; JButton buttonOk, buttonCopyAll, buttonBrowse; - JPanel panelButton, checkPanel, radioPanel; - JOptionPane panelButtonDialog, panelNoDupAnnDialog; - JDialog dialogAfterButton, noDupAnnDialog; - LayoutManager layout; - StringBuffer choices; - JRadioButton radioButtonA, radioButtonB; - String newline = "\n"; - JTextField textField; + JDialog noDupAnnDialog; JTextArea textArea; JFileChooser fileChooser; File file, directory; + // Constructor public BatchProcessLogFrame() { - // write code here this.frame = new JFrame("Batch Process Log"); - this.layout = new FlowLayout(); createGUI(); } private void createGUI() { + JOptionPane panelNoDupAnnDialog; + frame.setLayout(new GridBagLayout()); - // create buttonOK - buttonOk = new JButton("Ok to Close"); - // add button to actionListener + // create buttons and add top actionListener + buttonOk = new JButton("Ok"); buttonOk.addActionListener(this); - // add buttonCopyAll - buttonCopyAll = new JButton("Copy All"); - //buttonCopyAll.setBounds(70, 270, 150, 50); - //buttonCopyAll.setPreferredSize(new Dimension(200,500)); + buttonCopyAll = new JButton("Copy"); buttonCopyAll.addActionListener(this); - // add buttonBrowse - buttonBrowse = new JButton("Browse to save"); - //buttonBrowse.setBounds(70, 270, 150, 50); - //buttonBrowse.setPreferredSize(new Dimension(200,500)); + buttonBrowse = new JButton("Save"); buttonBrowse.addActionListener(this); - // create JPanel for the button - panelButton = new JPanel(); - panelButton.add(buttonOk); // Add button to JPanel - panelButton.add(buttonCopyAll); - panelButton.add(buttonBrowse); - // a dialog pop out when there is no content in the textArea panelNoDupAnnDialog = new JOptionPane("No duplicated annotations are found!"); noDupAnnDialog = panelNoDupAnnDialog.createDialog("Dialog"); @@ -99,29 +75,29 @@ private void createGUI() { c.gridy = 1; c.gridheight = 1; c.gridwidth = 1; - c.weightx = 1; // distribute space + c.weightx = 1; c.weighty = 0; - frame.add(buttonCopyAll, c); // add buttonCopyAll to the frame + frame.add(buttonCopyAll, c); // Anchor the buttonBrowse to the right (EAST) of the frame c.anchor = GridBagConstraints.EAST; c.fill = GridBagConstraints.NONE; c.gridx = 2; c.gridy = 1; - c.gridheight = 1; // see the height of the button the same as the height of checkbox1 + checkbox2 + c.gridheight = 1; c.weightx = 1; - frame.add(buttonBrowse, c); // add button to the frame + frame.add(buttonBrowse, c); // Anchor the "OK" button to the CENTER of the frame c.anchor = GridBagConstraints.CENTER; c.fill = GridBagConstraints.NONE; c.gridx = 1; c.gridy = 2; - c.gridheight = 2; // see the height of the button the same as the height of checkbox1 + checkbox2 + c.gridheight = 2; c.weightx = 1; - frame.add(buttonOk, c); // add button to the frame + frame.add(buttonOk, c); - // Display the window. + // Display the frame. frame.setBounds(80, 80, 80, 80); frame.pack(); frame.setVisible(true); @@ -131,6 +107,7 @@ private void createGUI() { @Override public void actionPerformed(ActionEvent e) { + String newline = "\n"; String data = textArea.getText().trim(); // read contents of text area into 'data' if (!data.equals("")) { @@ -143,9 +120,6 @@ public void actionPerformed(ActionEvent e) { clpbrd.setContents(stringSelection, null); JOptionPane.showMessageDialog(null, "Texts are copied to clipboard!"); } else if (e.getSource() == buttonBrowse) { - // fileChooser = new JFileChooser("C:\\Users\\chend\\Desktop"); - // fileChooser = new JFileChooser(latticeStraighten.outputDirectory); - fileChooser.setSelectedFile(new File("Duplicated_Annotations.txt")); int returnVal = fileChooser.showSaveDialog(frame); @@ -154,12 +128,17 @@ public void actionPerformed(ActionEvent e) { // save to file try (FileWriter fw = new FileWriter(fileChooser.getSelectedFile())) { fw.write(textArea.getText()); + textArea.append("\n\n <<" + file.getName() + ">> is saved to: " + + fileChooser.getSelectedFile().getAbsolutePath() + newline); + } catch (FileNotFoundException ex) { + JOptionPane panelSaveFailedDialog = new JOptionPane("File save failed! " + "\n" + ex); + JDialog saveFailedDialog = panelSaveFailedDialog.createDialog("Dialog"); + saveFailedDialog.setModalityType(ModalityType.APPLICATION_MODAL); + saveFailedDialog.setVisible(true); + } catch (Exception ex) { ex.printStackTrace(); } - - textArea.append("\n\n <<" + file.getName() + ">> is saved to: " - + fileChooser.getSelectedFile().getAbsolutePath() + newline); } } } else { From 3df941bd93b83d25050ba69519739677adf828c9 Mon Sep 17 00:00:00 2001 From: chend Date: Fri, 31 May 2024 16:39:22 -0400 Subject: [PATCH 19/39] Added chartPanel and features to move the marker along with the mouse dragged and reflects the values --- .../PlugInDialogVolumeRenderDualJanelia.java | 138 ++++++++++-------- 1 file changed, 81 insertions(+), 57 deletions(-) diff --git a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java index e9ef13e748..3c373ff28d 100644 --- a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java +++ b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java @@ -85,8 +85,10 @@ import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionAdapter; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; +import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; @@ -98,6 +100,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Vector; @@ -127,7 +130,11 @@ import org.janelia.mipav.test.Plot; import org.jfree.chart.ChartFactory; +import org.jfree.chart.ChartPanel; import org.jfree.chart.JFreeChart; +import org.jfree.chart.axis.ValueAxis; +import org.jfree.chart.event.MarkerChangeEvent; +import org.jfree.chart.event.MarkerChangeListener; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.ValueMarker; import org.jfree.chart.plot.XYPlot; @@ -2199,12 +2206,12 @@ private void annotationAnimationFromSpreadSheet() { progress.dispose(); progress = null; } - - private JLabel plotLabel; - private JPanel plotPanel; + + private JFreeChart selectionChart; + private ChartPanel chartPanel; // create a chart image from the values obtained via annotations - public static JFreeChart createChart(List values, String title) throws IOException { + public JFreeChart createChart(List values, String title) { XYSeries series = new XYSeries("Data"); float maxValue = -Float.MAX_VALUE; int maxIndex = -1; @@ -2239,31 +2246,36 @@ public static JFreeChart createChart(List values, String title) throws IO marker.setLabelTextAnchor(TextAnchor.CENTER_LEFT); plot.addDomainMarker(marker); - + marker.addChangeListener(new MarkerChangeListener() { + @Override + public void markerChanged(MarkerChangeEvent event) { + System.out.println("Marker changed"); + } + }); + return chart; - } - - JFreeChart selectionChart; + } // update the plot panel in responding to the clicking - public void updatePlotPanel(List values, String title) { - SwingUtilities.invokeLater(() -> { - try { - selectionChart = createChart(values, title); - - BufferedImage chartImage = new BufferedImage(400, 600, BufferedImage.TYPE_INT_RGB); - Graphics2D g2 = chartImage.createGraphics(); - selectionChart.draw(g2, new Rectangle(0, 0, 400, 600)); - g2.dispose(); - - plotLabel.setIcon(new ImageIcon(chartImage)); - plotPanel.revalidate(); - plotPanel.repaint(); - } catch (IOException e) { - System.err.println("Error updating plot: " + e.getMessage()); - } - }); - } + public void updatePlotPanel(List values, String title) { + SwingUtilities.invokeLater(() -> { + selectionChart = createChart(values, title); + chartPanel.setChart(selectionChart); + + + chartPanel.setPreferredSize(new Dimension(600, 400)); + chartPanel.setMouseWheelEnabled(true); + + chartPanel.setDomainZoomable(false); + chartPanel.setRangeZoomable(false); + chartPanel.setMouseZoomable(false, false); + chartPanel.setFillZoomRectangle(false); + chartPanel.setZoomAroundAnchor(false); + + chartPanel.revalidate(); + chartPanel.repaint(); + }); + } /** @@ -2391,39 +2403,51 @@ public void actionPerformed(ActionEvent e) { // Add the accurateModeButton to the buttonPanel, then add the buttonPanel to // the accuratePanel buttonPanel.add(accurateModeButton); - accuratePanel.add(buttonPanel, BorderLayout.CENTER); - - plotPanel = new JPanel(new BorderLayout()); - plotPanel.setBorder(new TitledBorder("Plot")); - plotLabel = new JLabel(); - plotLabel.addMouseMotionListener(new MouseAdapter() { - public void mouseDragged(MouseEvent e) { - Point p = e.getPoint(); - System.out.println(p); - - Rectangle plotArea = new Rectangle(0, 0, 400, 600); - XYPlot plot = (XYPlot) selectionChart.getPlot(); - Collection dm = plot.getDomainMarkers(Layer.FOREGROUND); - ValueMarker firstDM = (ValueMarker) dm.toArray()[0]; - double xValue = plot.getDomainAxis().java2DToValue(p.getX(), plotArea, plot.getDomainAxisEdge()); - firstDM.setValue(xValue); - - XYSeries series = ((XYSeriesCollection) plot.getDataset()).getSeries(0); - double yValue = series.getY((int) xValue).doubleValue(); - firstDM.setLabel("Value: " + yValue); + accuratePanel.add(buttonPanel, BorderLayout.NORTH); - BufferedImage chartImage = new BufferedImage(400, 600, BufferedImage.TYPE_INT_RGB); - Graphics2D g2 = chartImage.createGraphics(); - selectionChart.draw(g2, new Rectangle(0, 0, 400, 600)); - g2.dispose(); - - plotLabel.setIcon(new ImageIcon(chartImage)); - plotPanel.revalidate(); - plotPanel.repaint(); - } + selectionChart = createChart(Arrays.asList(1.0f, 2.0f, 3.0f), "Chart"); + chartPanel = new ChartPanel(selectionChart); + + chartPanel.addMouseMotionListener(new MouseAdapter() { + @Override + public void mouseDragged(MouseEvent e) { + + Rectangle2D plotArea = chartPanel.getScreenDataArea(); + XYPlot plot = selectionChart.getXYPlot(); + ValueAxis xAxis = plot.getDomainAxis(); + double x = xAxis.java2DToValue(e.getX(), plotArea, plot.getDomainAxisEdge()); + + // Calculate the corresponding Y-value + XYSeriesCollection dataset = (XYSeriesCollection) plot.getDataset(); + XYSeries series = dataset.getSeries(0); + + int index = findNearestXIndex(series, x); + double y = series.getY(index).doubleValue(); + + ValueMarker marker = (ValueMarker) plot.getDomainMarkers(Layer.FOREGROUND).iterator().next(); + marker.setValue(x); + marker.setLabel(String.format("Value: %.2f", y)); + + chartPanel.repaint(); + e.consume(); + } + + private int findNearestXIndex(XYSeries series, double x) { + double minDistance = Double.MAX_VALUE; + int nearestIndex = -1; + + for (int i = 0; i < series.getItemCount(); i++) { + double distance = Math.abs(series.getX(i).doubleValue() - x); + if (distance < minDistance) { + minDistance = distance; + nearestIndex = i; + } + } + return nearestIndex; + } }); - plotPanel.add(plotLabel, BorderLayout.CENTER); - accuratePanel.add(plotPanel, BorderLayout.SOUTH); + + accuratePanel.add(chartPanel, BorderLayout.CENTER); tabbedPane = new JTabbedPane(); tabbedPane.addTab("LUT", null, lutPanel); From 96a934d581c94ee68a5973b47d51aa03877accff Mon Sep 17 00:00:00 2001 From: chend Date: Mon, 3 Jun 2024 17:04:38 -0400 Subject: [PATCH 20/39] added function to be able to move the marker in the plot to move the 3D marker along the array. --- .../WildMagic/VolumeTriPlanarRender.java | 13 ++-- .../plugins/worm/untwisting/PlotListener.java | 5 +- .../PlugInDialogVolumeRenderDualJanelia.java | 68 ++++++++++++++++--- 3 files changed, 73 insertions(+), 13 deletions(-) diff --git a/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java b/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java index 3d7c722c48..a7f236c012 100644 --- a/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java +++ b/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java @@ -859,6 +859,8 @@ private void PickVolume3D(Vector3f kPos, Vector3f kDir, Vector3f maxPtAccurate) { List plotAccurateValues = new ArrayList<>(); List plotValues = new ArrayList<>(); + List points = new ArrayList<>(); + m_kPicker.Execute(m_kVolumeRayCast.GetScene(),kPos,kDir,0.0f, Float.MAX_VALUE); @@ -1006,6 +1008,9 @@ private void PickVolume3D(Vector3f kPos, Vector3f kDir, Vector3f maxPtAccurate) plotValues.add(value); output.writeData(p0.X, p0.Y, p0.Z, value); } + Vector3f p2 = new Vector3f(); + p2.copy(p0); + points.add(p2); } @@ -1073,9 +1078,9 @@ else if ( !doAutomaticLabels() ) } } if (m_kVOIInterface.isAccurateMode()) { - setPlot(plotAccurateValues, "Accurate Values"); + setPlot(points, plotAccurateValues, "Accurate Values"); } else { - setPlot(plotValues, "3-Color Values"); + setPlot(points, plotValues, "3-Color Values"); } } @@ -1083,9 +1088,9 @@ else if ( !doAutomaticLabels() ) private Set listeners = new HashSet<>(); // update all listeners with new plot values and title. - public void setPlot(List values, String title) { + public void setPlot(List points, List values, String title) { for (PlotListener listener : listeners) { - listener.updatePlotPanel(values, title); + listener.updatePlotPanel(points, values, title); } } diff --git a/src/org/janelia/mipav/plugins/worm/untwisting/PlotListener.java b/src/org/janelia/mipav/plugins/worm/untwisting/PlotListener.java index b080398fa8..ab3ca37e59 100644 --- a/src/org/janelia/mipav/plugins/worm/untwisting/PlotListener.java +++ b/src/org/janelia/mipav/plugins/worm/untwisting/PlotListener.java @@ -2,6 +2,8 @@ import java.util.List; +import WildMagic.LibFoundation.Mathematics.Vector3f; + /** * Interface for handling updates to a plot panel. Classes that need to respond * to plot data changes should implement this interface. An instance of a class @@ -16,12 +18,13 @@ public interface PlotListener { /** * Invoked to update the plot panel with new data. + * @param points * * @param values The list of data values to plot. Each value represents a data * point on the plot. * @param title The title of the plot, describing the data or context of what * is being displayed */ - void updatePlotPanel(List values, String title); + void updatePlotPanel(List points, List values, String title); } diff --git a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java index 3c373ff28d..49026fe9fa 100644 --- a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java +++ b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java @@ -2209,6 +2209,7 @@ private void annotationAnimationFromSpreadSheet() { private JFreeChart selectionChart; private ChartPanel chartPanel; + private List chart3DPoints; // create a chart image from the values obtained via annotations public JFreeChart createChart(List values, String title) { @@ -2237,6 +2238,7 @@ public JFreeChart createChart(List values, String title) { renderer.setSeriesPaint(0, Color.BLUE); renderer.setSeriesStroke(0, new BasicStroke(2.0f)); plot.setRenderer(renderer); + // Add a marker to show the maximum value ValueMarker marker = new ValueMarker(maxIndex); @@ -2246,18 +2248,65 @@ public JFreeChart createChart(List values, String title) { marker.setLabelTextAnchor(TextAnchor.CENTER_LEFT); plot.addDomainMarker(marker); - marker.addChangeListener(new MarkerChangeListener() { - @Override - public void markerChanged(MarkerChangeEvent event) { - System.out.println("Marker changed"); - } - }); + marker.addChangeListener(new MarkerChangeListener() { + @Override + public void markerChanged(MarkerChangeEvent event) { + ValueMarker marker = (ValueMarker) event.getMarker(); + + float tq = (float) marker.getValue(); + + int t0 = (int) Math.floor(tq); + int t1 = (int) Math.ceil(tq); + + if (t0 >= 0 && t1 < chart3DPoints.size() && t0 != t1) { + + Vector3f interpolatedPoint = interpolate(t0, t1, tq); + System.out.println("Interpolated 3D Point at index: " + tq + " is ptq: " + interpolatedPoint); + update3DModel(interpolatedPoint); + + } else if (t0 == t1 && t0 < chart3DPoints.size()) { // if t0 == t1 + Vector3f exactPoint = chart3DPoints.get(t0); + System.out.println("Exact 3D Point at index: " + t0 + " is ptq: " + exactPoint); + update3DModel(exactPoint); + } + } + - return chart; + private void update3DModel(Vector3f point) { + + //add something + System.out.println("Updating 3D model for point: " + point); + activeImage.voiManager.modify3DMarker(point, point, point); + } + + public Vector3f interpolate(int t0, int t1, float tq) { + Vector3f pt0 = chart3DPoints.get(t0); + Vector3f pt1 = chart3DPoints.get(t1); + System.out.println("pt0: "+ pt0); + System.out.println("pt1: " + pt1); + float m0 = (t1 - tq) / (t1 - t0); + float m1 = (tq - t0) / (t1 - t0); + + float x = (float) (m0 * pt0.X + m1 * pt1.X); + float y = (float) (m0 * pt0.Y + m1 * pt1.Y); + float z = (float) (m0 * pt0.Z + m1 * pt1.Z); + + //pt0 = pt0.scale(m0); + //pt1 = pt1.scale(m1); + //Vector3f ptq = Vector3f.add(pt0, pt1); + + Vector3f ptq = new Vector3f(x, y, z); + + return ptq; + } + }); + + return chart; } + // update the plot panel in responding to the clicking - public void updatePlotPanel(List values, String title) { + public void updatePlotPanel(List points, List values, String title) { SwingUtilities.invokeLater(() -> { selectionChart = createChart(values, title); chartPanel.setChart(selectionChart); @@ -2271,6 +2320,9 @@ public void updatePlotPanel(List values, String title) { chartPanel.setMouseZoomable(false, false); chartPanel.setFillZoomRectangle(false); chartPanel.setZoomAroundAnchor(false); + + chart3DPoints = points; + chartPanel.revalidate(); chartPanel.repaint(); From 79286d27b887e12998757c36488502efb1dbc9f2 Mon Sep 17 00:00:00 2001 From: chend Date: Tue, 4 Jun 2024 14:38:13 -0400 Subject: [PATCH 21/39] Fixed the first 3D marker so that it would move along with mouseDrag from the plot --- .../view/renderer/WildMagic/WormUntwisting/LatticeModel.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gov/nih/mipav/view/renderer/WildMagic/WormUntwisting/LatticeModel.java b/src/gov/nih/mipav/view/renderer/WildMagic/WormUntwisting/LatticeModel.java index 0076dc92d0..6b06bac9db 100644 --- a/src/gov/nih/mipav/view/renderer/WildMagic/WormUntwisting/LatticeModel.java +++ b/src/gov/nih/mipav/view/renderer/WildMagic/WormUntwisting/LatticeModel.java @@ -8427,6 +8427,7 @@ private void updateLattice(final boolean rebuild) { } if (right.getCurves().size() == 0) { updateLatticeListeners(); + updateSelected(); return; } From 5fb7f7a67a96c28647b1844b0b4c1a540cf15e9e Mon Sep 17 00:00:00 2001 From: chend Date: Tue, 4 Jun 2024 15:42:50 -0400 Subject: [PATCH 22/39] ValueOutput class is deleted due to no longer needed --- test/org/janelia/mipav/test/ValueOutput.java | 44 -------------------- 1 file changed, 44 deletions(-) delete mode 100644 test/org/janelia/mipav/test/ValueOutput.java diff --git a/test/org/janelia/mipav/test/ValueOutput.java b/test/org/janelia/mipav/test/ValueOutput.java deleted file mode 100644 index aadf11a6b6..0000000000 --- a/test/org/janelia/mipav/test/ValueOutput.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.janelia.mipav.test; - - -import java.io.FileWriter; -import java.io.IOException; - -public class ValueOutput { - private FileWriter fileWriter; - - public ValueOutput(String fileName) { - try { - this.fileWriter = new FileWriter(fileName); - // Writing the header - this.fileWriter.append("p0.X,p0.Y,p0.Z,value\n"); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - } - - public void writeData(float x, float y, float z, float value) { - // Writing data in the CSV format - try { - this.fileWriter.append(String.format("%.1f,%.1f,%.1f,%.1f\n", x, y, z, value)); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - - public void close() { - // Closing the FileWriter - try { - this.fileWriter.close(); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } -} - - - From 7fde24f6451bd3906b1da13638391d3fa9008544 Mon Sep 17 00:00:00 2001 From: chend Date: Tue, 4 Jun 2024 15:44:28 -0400 Subject: [PATCH 23/39] comment added to explain adding updateSelected for fix the first 3D points to be movable when mouseDrag the marker in the plot --- .../view/renderer/WildMagic/WormUntwisting/LatticeModel.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gov/nih/mipav/view/renderer/WildMagic/WormUntwisting/LatticeModel.java b/src/gov/nih/mipav/view/renderer/WildMagic/WormUntwisting/LatticeModel.java index 6b06bac9db..5e0db20054 100644 --- a/src/gov/nih/mipav/view/renderer/WildMagic/WormUntwisting/LatticeModel.java +++ b/src/gov/nih/mipav/view/renderer/WildMagic/WormUntwisting/LatticeModel.java @@ -8427,6 +8427,8 @@ private void updateLattice(final boolean rebuild) { } if (right.getCurves().size() == 0) { updateLatticeListeners(); + // added updateSelected() to fix the movable of first 3D marker to reflects the + // mourseDrag of the marker in the plot updateSelected(); return; } From 00996d7b2647909c1c957a13bb26b814622cfb01 Mon Sep 17 00:00:00 2001 From: chend Date: Tue, 4 Jun 2024 15:49:47 -0400 Subject: [PATCH 24/39] added comments and documentations as well as deleted elements used for debugging and valueOutput --- .../VOI/VOILatticeManagerInterface.java | 59 +++-- .../WildMagic/VolumeTriPlanarRender.java | 20 +- .../PlugInDialogVolumeRenderDualJanelia.java | 240 ++++++++++-------- 3 files changed, 174 insertions(+), 145 deletions(-) diff --git a/src/gov/nih/mipav/view/renderer/WildMagic/VOI/VOILatticeManagerInterface.java b/src/gov/nih/mipav/view/renderer/WildMagic/VOI/VOILatticeManagerInterface.java index 933e948e85..5c3cc9de37 100644 --- a/src/gov/nih/mipav/view/renderer/WildMagic/VOI/VOILatticeManagerInterface.java +++ b/src/gov/nih/mipav/view/renderer/WildMagic/VOI/VOILatticeManagerInterface.java @@ -1096,66 +1096,73 @@ private void setVoxelSize() defaultVoxelSize = new JTextField( "" + VoxelSize ); defaultVoxelSize.addActionListener(this); JPanel panel = new JPanel( new GridLayout(1, 3) ); - panel.add( new JLabel( "Current Voxel Size" ) ); - panel.add( defaultVoxelSize ); - panel.add( new JLabel("um") ); + panel.add(new JLabel("Current Voxel Size")); + panel.add(defaultVoxelSize); + panel.add(new JLabel("um")); updateVoxelSize = new JDialog(); updateVoxelSize.getContentPane().setLayout(new BorderLayout()); - updateVoxelSize.setModalityType( JDialog.ModalityType.APPLICATION_MODAL); - updateVoxelSize.getContentPane().add( panel, BorderLayout.NORTH ); - updateVoxelSize.getContentPane().add( OK, BorderLayout.SOUTH ); + updateVoxelSize.setModalityType(JDialog.ModalityType.APPLICATION_MODAL); + updateVoxelSize.getContentPane().add(panel, BorderLayout.NORTH); + updateVoxelSize.getContentPane().add(OK, BorderLayout.SOUTH); updateVoxelSize.pack(); updateVoxelSize.setResizable(false); MipavUtil.centerOnScreen(updateVoxelSize); updateVoxelSize.setVisible(true); } - - public void mouseReleased(MouseEvent e) { + + // Handle event when mouse button is released + public void mouseReleased(MouseEvent e) { movingPickedPoint = false; - if(editingCrossSections) { + if (editingCrossSections) { latticeModel.showLattice(true); } - } + } + // Handle key press events if the key is pressed public void keyPressed(KeyEvent e) { isShiftSelected = e.isShiftDown(); } - - + + // Maintains state for accurate mode and listeners for its changes private boolean accurateMode = true; - // TODO: might want to change the array into set - private List listeners = new ArrayList<>(); - - public boolean isAccurateMode() { - return accurateMode; - } + // List of listeners that will be notified when accurate mode changes + // TODO: might want to change the array into set + private List listeners = new ArrayList<>(); + // Check if the accurate mode is currently enabled + public boolean isAccurateMode() { + return accurateMode; + } + + // Toggle the accurate mode state and notify all listeners public void toggleAccurateMode() { accurateMode = !accurateMode; setAccurateMode(accurateMode); } - + + // Set the accurate mode and notify listeners about the mode change public void setAccurateMode(boolean accurateMode) { this.accurateMode = accurateMode; for (AccurateModeListener listener : listeners) { listener.accurateModeChanged(accurateMode); } } - - public void addAccurateModeListener(AccurateModeListener listener) { - listeners.add(listener); - } - + // Add a new listener to be notified about accurate mode changes + public void addAccurateModeListener(AccurateModeListener listener) { + listeners.add(listener); + } + + // Handle key release events and perform actions based on the key released public void keyReleased(KeyEvent e) { isShiftSelected = e.isShiftDown(); movingPickedPoint = false; - System.out.println(e.getKeyChar()); - + + // Specific actions based on key codes, e.g., toggle accurate mode on 'M' key if (e.getKeyCode() == KeyEvent.VK_M) { toggleAccurateMode(); } diff --git a/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java b/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java index a7f236c012..072cc22049 100644 --- a/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java +++ b/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java @@ -68,7 +68,6 @@ import org.janelia.mipav.plugins.worm.untwisting.AccurateModeListener; import org.janelia.mipav.plugins.worm.untwisting.PlotListener; import org.janelia.mipav.plugins.worm.untwisting.PlugInDialogVolumeRenderDualJanelia; -import org.janelia.mipav.test.ValueOutput; import WildMagic.LibFoundation.Mathematics.ColorRGBA; import WildMagic.LibFoundation.Mathematics.Mathf; @@ -857,11 +856,11 @@ else if ( m_bErase ) private void PickVolume3D(Vector3f kPos, Vector3f kDir, Vector3f maxPtAccurate) { + // Lists to store values and points for plot updates or further processing List plotAccurateValues = new ArrayList<>(); List plotValues = new ArrayList<>(); List points = new ArrayList<>(); - m_kPicker.Execute(m_kVolumeRayCast.GetScene(),kPos,kDir,0.0f, Float.MAX_VALUE); @@ -874,8 +873,6 @@ private void PickVolume3D(Vector3f kPos, Vector3f kDir, Vector3f maxPtAccurate) float distances[] = new float[m_kPicker.Records.size()]; long time = System.currentTimeMillis(); - ValueOutput outputAccurate = new ValueOutput("outputAccurate" + time + ".csv"); - ValueOutput output = new ValueOutput("output" + time + ".csv"); for ( int i = 0; i < m_kPicker.Records.size(); i++ ) { @@ -970,9 +967,7 @@ private void PickVolume3D(Vector3f kPos, Vector3f kDir, Vector3f maxPtAccurate) maxValueAccurate = valueAccurate; maxPtAccurate.copy(p0); } - plotAccurateValues.add(valueAccurate); - // Write data to CSV - outputAccurate.writeData(p0.X, p0.Y, p0.Z, valueAccurate); + plotAccurateValues.add(valueAccurate);// add value to the list for plotting } else { //this is not working, made for more than 3 colors. -Diyi Chen May 1, 2024 @@ -1005,21 +1000,12 @@ private void PickVolume3D(Vector3f kPos, Vector3f kDir, Vector3f maxPtAccurate) maxValue = value; maxPt.copy(p0); } - plotValues.add(value); - output.writeData(p0.X, p0.Y, p0.Z, value); + plotValues.add(value);// add value to the list for plotting } Vector3f p2 = new Vector3f(); p2.copy(p0); points.add(p2); } - - - output.writeData(maxPt.X, maxPt.Y, maxPt.Z, maxValue); - outputAccurate.writeData(maxPtAccurate.X, maxPtAccurate.Y, maxPtAccurate.Z, maxValueAccurate); - - // Close the output stream - outputAccurate.close(); - output.close(); if (!m_kVOIInterface.isAccurateMode()) { maxValueAccurate = maxValue; diff --git a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java index 49026fe9fa..fef6397357 100644 --- a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java +++ b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java @@ -1350,6 +1350,15 @@ public boolean updateImages(ModelLUT LUTa, ModelLUT LUTb, boolean flag, int inte return false; } + /** + * Responds to changes in accurate mode by updating the button's appearance and + * text to reflect the current mode state. This method is typically called by a + * listener that is notified when accurate mode settings are toggled elsewhere + * in the application. + * + * @param isAccurateMode A boolean indicating whether accurate mode is enabled + * (true) or disabled (false). + */ @Override public void accurateModeChanged(boolean isAccurateMode) { accurateModeButton.setSelected(isAccurateMode); @@ -2211,43 +2220,51 @@ private void annotationAnimationFromSpreadSheet() { private ChartPanel chartPanel; private List chart3DPoints; - // create a chart image from the values obtained via annotations + /** + * Creates a chart using a list of values and assigns it a title. Each value in + * the list is plotted against its index. + * + * @param values List of floating-point values for the Y-axis. + * @param title Title of the chart. + * @return A JFreeChart object fully initialized. + */ public JFreeChart createChart(List values, String title) { - XYSeries series = new XYSeries("Data"); - float maxValue = -Float.MAX_VALUE; - int maxIndex = -1; - - for (int i = 0; i < values.size(); i++) { - float value = values.get(i); - series.add(i, value); - if (value > maxValue) { - maxValue = value; - maxIndex = i; - } - } - - XYSeriesCollection dataset = new XYSeriesCollection(); - dataset.addSeries(series); - - JFreeChart chart = ChartFactory.createXYLineChart( - title, "Index", "Value", dataset, - PlotOrientation.VERTICAL, true, true, false); - - XYPlot plot = chart.getXYPlot(); - XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(); - renderer.setSeriesPaint(0, Color.BLUE); - renderer.setSeriesStroke(0, new BasicStroke(2.0f)); - plot.setRenderer(renderer); - - - // Add a marker to show the maximum value - ValueMarker marker = new ValueMarker(maxIndex); - marker.setPaint(Color.RED); - marker.setLabel("Max Value: " + maxValue); - marker.setLabelAnchor(RectangleAnchor.CENTER); - marker.setLabelTextAnchor(TextAnchor.CENTER_LEFT); - plot.addDomainMarker(marker); - + XYSeries series = new XYSeries("Data"); + float maxValue = -Float.MAX_VALUE; + int maxIndex = -1; + + // Populate the series with values and track the maximum value and its index + for (int i = 0; i < values.size(); i++) { + float value = values.get(i); + series.add(i, value); + if (value > maxValue) { + maxValue = value; + maxIndex = i; + } + } + + XYSeriesCollection dataset = new XYSeriesCollection(); + dataset.addSeries(series); + + // Create the chart + JFreeChart chart = ChartFactory.createXYLineChart(title, "Index", "Value", dataset, PlotOrientation.VERTICAL, + true, true, false); + + XYPlot plot = chart.getXYPlot(); + XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(); + renderer.setSeriesPaint(0, Color.BLUE); + renderer.setSeriesStroke(0, new BasicStroke(2.0f)); + plot.setRenderer(renderer); + + // Add a dynamic marker at the position of the maximum value + ValueMarker marker = new ValueMarker(maxIndex); + marker.setPaint(Color.RED); + marker.setLabel("Max Value: " + maxValue); + marker.setLabelAnchor(RectangleAnchor.CENTER); + marker.setLabelTextAnchor(TextAnchor.CENTER_LEFT); + plot.addDomainMarker(marker); + + // Set up a marker change listener to handle marker position changes marker.addChangeListener(new MarkerChangeListener() { @Override public void markerChanged(MarkerChangeEvent event) { @@ -2255,34 +2272,43 @@ public void markerChanged(MarkerChangeEvent event) { float tq = (float) marker.getValue(); + // Calculate the indices for interpolation int t0 = (int) Math.floor(tq); int t1 = (int) Math.ceil(tq); - + if (t0 >= 0 && t1 < chart3DPoints.size() && t0 != t1) { Vector3f interpolatedPoint = interpolate(t0, t1, tq); - System.out.println("Interpolated 3D Point at index: " + tq + " is ptq: " + interpolatedPoint); update3DModel(interpolatedPoint); - + } else if (t0 == t1 && t0 < chart3DPoints.size()) { // if t0 == t1 Vector3f exactPoint = chart3DPoints.get(t0); - System.out.println("Exact 3D Point at index: " + t0 + " is ptq: " + exactPoint); update3DModel(exactPoint); } } - + /** + * Updates the 3D model visualization based on a new 3D point. + * + * @param point The 3D point that the model needs to reflect. + */ private void update3DModel(Vector3f point) { - - //add something - System.out.println("Updating 3D model for point: " + point); activeImage.voiManager.modify3DMarker(point, point, point); } + /** + * Interpolates between two 3D points based on a given interpolation parameter. + * + * @param t0 Index of the first point. + * @param t1 Index of the second point. + * @param tq The interpolation parameter, typically derived from the marker's + * position. + * @return The interpolated 3D point. + */ public Vector3f interpolate(int t0, int t1, float tq) { Vector3f pt0 = chart3DPoints.get(t0); Vector3f pt1 = chart3DPoints.get(t1); - System.out.println("pt0: "+ pt0); + System.out.println("pt0: " + pt0); System.out.println("pt1: " + pt1); float m0 = (t1 - tq) / (t1 - t0); float m1 = (tq - t0) / (t1 - t0); @@ -2290,10 +2316,6 @@ public Vector3f interpolate(int t0, int t1, float tq) { float x = (float) (m0 * pt0.X + m1 * pt1.X); float y = (float) (m0 * pt0.Y + m1 * pt1.Y); float z = (float) (m0 * pt0.Z + m1 * pt1.Z); - - //pt0 = pt0.scale(m0); - //pt1 = pt1.scale(m1); - //Vector3f ptq = Vector3f.add(pt0, pt1); Vector3f ptq = new Vector3f(x, y, z); @@ -2303,32 +2325,35 @@ public Vector3f interpolate(int t0, int t1, float tq) { return chart; } - - // update the plot panel in responding to the clicking + /** + * Updates the plot panel by invoking the chart creation and updating the + * display. + * + * @param points List of 3D points for the plot. + * @param values List of values corresponding to each 3D point. + * @param title Title for the updated plot. + */ public void updatePlotPanel(List points, List values, String title) { - SwingUtilities.invokeLater(() -> { - selectionChart = createChart(values, title); - chartPanel.setChart(selectionChart); - - - chartPanel.setPreferredSize(new Dimension(600, 400)); - chartPanel.setMouseWheelEnabled(true); - - chartPanel.setDomainZoomable(false); - chartPanel.setRangeZoomable(false); - chartPanel.setMouseZoomable(false, false); - chartPanel.setFillZoomRectangle(false); - chartPanel.setZoomAroundAnchor(false); - - chart3DPoints = points; - - - chartPanel.revalidate(); - chartPanel.repaint(); - }); - } + SwingUtilities.invokeLater(() -> { + selectionChart = createChart(values, title); + chartPanel.setChart(selectionChart); + chartPanel.setPreferredSize(new Dimension(600, 400)); + chartPanel.setMouseWheelEnabled(true); + + chartPanel.setDomainZoomable(false); + chartPanel.setRangeZoomable(false); + chartPanel.setMouseZoomable(false, false); + chartPanel.setFillZoomRectangle(false); + chartPanel.setZoomAroundAnchor(false); + + chart3DPoints = points; + + chartPanel.revalidate(); + chartPanel.repaint(); + }); + } /** * User-interface initialization. If the UI is integrated all panels are @@ -2431,11 +2456,11 @@ private void init() { opacityPanel.add(opacityTab, BorderLayout.CENTER); clipPanel = new JPanel(new BorderLayout()); - // added a panel with button to be able to turn off accurate mode and switch to - // 3-color mode(not working currently) + // Create a panel for toggling between accurate and 3-color modes(not working + // currently) accuratePanel = new JPanel(new BorderLayout()); JPanel buttonPanel = new JPanel(); - // accurateModeButton = new JButton("Accurate Mode"); + // Initialize a toggle button for switching modes accurateModeButton = new JToggleButton("Accurate Mode"); accurateModeChanged(true); accurateModeButton.setPreferredSize(new Dimension(200, 50)); @@ -2444,62 +2469,71 @@ private void init() { accurateModeButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - // activeImage.voiManager.toggleAccurateMode(); - JToggleButton toggleButton = (JToggleButton) e.getSource(); boolean isSelected = toggleButton.isSelected(); activeImage.voiManager.toggleAccurateMode(); } }); - // Add the accurateModeButton to the buttonPanel, then add the buttonPanel to - // the accuratePanel + // Add the toggle button to the button panel and then add the panel to the + // accurate mode panel buttonPanel.add(accurateModeButton); accuratePanel.add(buttonPanel, BorderLayout.NORTH); - + + // Create a chart selectionChart = createChart(Arrays.asList(1.0f, 2.0f, 3.0f), "Chart"); chartPanel = new ChartPanel(selectionChart); + // Add a mouse motion listener to handle dragging movements over the chart chartPanel.addMouseMotionListener(new MouseAdapter() { @Override public void mouseDragged(MouseEvent e) { - + // Get the plot area for accurate coordinate calculation Rectangle2D plotArea = chartPanel.getScreenDataArea(); XYPlot plot = selectionChart.getXYPlot(); ValueAxis xAxis = plot.getDomainAxis(); double x = xAxis.java2DToValue(e.getX(), plotArea, plot.getDomainAxisEdge()); - // Calculate the corresponding Y-value + // Determine the corresponding Y-value by finding the nearest index XYSeriesCollection dataset = (XYSeriesCollection) plot.getDataset(); XYSeries series = dataset.getSeries(0); - int index = findNearestXIndex(series, x); - double y = series.getY(index).doubleValue(); - + int index = findNearestXIndex(series, x); + double y = series.getY(index).doubleValue(); + + // Update the marker's value and label on the chart ValueMarker marker = (ValueMarker) plot.getDomainMarkers(Layer.FOREGROUND).iterator().next(); marker.setValue(x); - marker.setLabel(String.format("Value: %.2f", y)); + marker.setLabel(String.format("Value: %.2f", y)); + // Redraw the chart to reflect changes chartPanel.repaint(); e.consume(); } - + + /** + * Finds the index of the closest X value to the given target x. + * + * @param series The series of data points. + * @param x The target x value to match. + * @return The index of the closest x value. + */ private int findNearestXIndex(XYSeries series, double x) { - double minDistance = Double.MAX_VALUE; - int nearestIndex = -1; - - for (int i = 0; i < series.getItemCount(); i++) { - double distance = Math.abs(series.getX(i).doubleValue() - x); - if (distance < minDistance) { - minDistance = distance; - nearestIndex = i; - } - } - return nearestIndex; - } + double minDistance = Double.MAX_VALUE; + int nearestIndex = -1; + + for (int i = 0; i < series.getItemCount(); i++) { + double distance = Math.abs(series.getX(i).doubleValue() - x); + if (distance < minDistance) { + minDistance = distance; + nearestIndex = i; + } + } + return nearestIndex; + } }); - accuratePanel.add(chartPanel, BorderLayout.CENTER); + accuratePanel.add(chartPanel, BorderLayout.CENTER); tabbedPane = new JTabbedPane(); tabbedPane.addTab("LUT", null, lutPanel); @@ -3533,9 +3567,11 @@ private void updateSurfacePanels() { // } } + /** + * Updates the selection panel method ensures that the user interface components + * related to VOI management reflect the current operational mode. + */ private void updateSelectionPanel() { - // accurateModeChanged(activeImage.voiManager.isAccurateMode()); - VOILatticeManagerInterface voiManager = activeImage.voiManager; voiManager.setAccurateMode(voiManager.isAccurateMode()); } From 1f44094a2b3cc5c05097f1482a368e6b0ad80881 Mon Sep 17 00:00:00 2001 From: chend Date: Tue, 4 Jun 2024 16:09:50 -0400 Subject: [PATCH 25/39] changed setPlot into more specific name: notifyPlotListeners --- .../view/renderer/WildMagic/VolumeTriPlanarRender.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java b/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java index 072cc22049..28b3b15685 100644 --- a/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java +++ b/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java @@ -1064,9 +1064,9 @@ else if ( !doAutomaticLabels() ) } } if (m_kVOIInterface.isAccurateMode()) { - setPlot(points, plotAccurateValues, "Accurate Values"); + notifyPlotListeners(points, plotAccurateValues, "Accurate Values"); } else { - setPlot(points, plotValues, "3-Color Values"); + notifyPlotListeners(points, plotValues, "3-Color Values"); } } @@ -1074,7 +1074,7 @@ else if ( !doAutomaticLabels() ) private Set listeners = new HashSet<>(); // update all listeners with new plot values and title. - public void setPlot(List points, List values, String title) { + public void notifyPlotListeners(List points, List values, String title) { for (PlotListener listener : listeners) { listener.updatePlotPanel(points, values, title); } From 6a44a835bbac7e88ea1eb42bf9462ad030795a8d Mon Sep 17 00:00:00 2001 From: chend Date: Tue, 4 Jun 2024 16:13:44 -0400 Subject: [PATCH 26/39] changed PlotListener into more specific name as PlotDataUpdateListener --- .../view/renderer/WildMagic/VolumeTriPlanarRender.java | 10 +++++----- .../{PlotListener.java => PlotDataUpdateListener.java} | 2 +- .../PlugInDialogVolumeRenderDualJanelia.java | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) rename src/org/janelia/mipav/plugins/worm/untwisting/{PlotListener.java => PlotDataUpdateListener.java} (96%) diff --git a/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java b/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java index 28b3b15685..36b2df8a8f 100644 --- a/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java +++ b/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java @@ -66,7 +66,7 @@ import javax.swing.KeyStroke; import org.janelia.mipav.plugins.worm.untwisting.AccurateModeListener; -import org.janelia.mipav.plugins.worm.untwisting.PlotListener; +import org.janelia.mipav.plugins.worm.untwisting.PlotDataUpdateListener; import org.janelia.mipav.plugins.worm.untwisting.PlugInDialogVolumeRenderDualJanelia; import WildMagic.LibFoundation.Mathematics.ColorRGBA; @@ -1071,22 +1071,22 @@ else if ( !doAutomaticLabels() ) } // Create a Set to hold PlotListener instances - private Set listeners = new HashSet<>(); + private Set listeners = new HashSet<>(); // update all listeners with new plot values and title. public void notifyPlotListeners(List points, List values, String title) { - for (PlotListener listener : listeners) { + for (PlotDataUpdateListener listener : listeners) { listener.updatePlotPanel(points, values, title); } } // add a new PlotListener - public void addPlotListener(PlotListener listener) { + public void addPlotListener(PlotDataUpdateListener listener) { listeners.add(listener); } // remove an existing PlotListener - public void removePlotListener(PlotListener listener) { + public void removePlotListener(PlotDataUpdateListener listener) { listeners.remove(listener); } diff --git a/src/org/janelia/mipav/plugins/worm/untwisting/PlotListener.java b/src/org/janelia/mipav/plugins/worm/untwisting/PlotDataUpdateListener.java similarity index 96% rename from src/org/janelia/mipav/plugins/worm/untwisting/PlotListener.java rename to src/org/janelia/mipav/plugins/worm/untwisting/PlotDataUpdateListener.java index ab3ca37e59..30e7ae3475 100644 --- a/src/org/janelia/mipav/plugins/worm/untwisting/PlotListener.java +++ b/src/org/janelia/mipav/plugins/worm/untwisting/PlotDataUpdateListener.java @@ -14,7 +14,7 @@ * * @author diyi chen */ -public interface PlotListener { +public interface PlotDataUpdateListener { /** * Invoked to update the plot panel with new data. diff --git a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java index fef6397357..967a4364ac 100644 --- a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java +++ b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java @@ -166,7 +166,7 @@ */ public class PlugInDialogVolumeRenderDualJanelia extends JFrame implements ActionListener, RendererListener, PropertyChangeListener, ViewImageUpdateInterface, WindowListener, - ChangeListener, AccurateModeListener, PlotListener { + ChangeListener, AccurateModeListener, PlotDataUpdateListener { private static final long serialVersionUID = -9056581285643263551L; From 8bf198bd0ca19501d221b3ddf84a4c423720e662 Mon Sep 17 00:00:00 2001 From: chend Date: Tue, 4 Jun 2024 16:26:20 -0400 Subject: [PATCH 27/39] moved the variables to the top of the class --- .../untwisting/PlugInDialogVolumeRenderDualJanelia.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java index 967a4364ac..5cf9994e81 100644 --- a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java +++ b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java @@ -285,6 +285,10 @@ public class PlugInDialogVolumeRenderDualJanelia extends JFrame private Vector annotationNames; private JSplitPane latticePanel = null; private JPanel latticePanelSingle = null; + + private JFreeChart selectionChart; + private ChartPanel chartPanel; + private List chart3DPoints; private class IntegratedWormData { private VOIVector annotations; @@ -2215,10 +2219,6 @@ private void annotationAnimationFromSpreadSheet() { progress.dispose(); progress = null; } - - private JFreeChart selectionChart; - private ChartPanel chartPanel; - private List chart3DPoints; /** * Creates a chart using a list of values and assigns it a title. Each value in From 81db71265b33a960ea10006673f5f802ca15678d Mon Sep 17 00:00:00 2001 From: chend Date: Wed, 5 Jun 2024 10:11:57 -0400 Subject: [PATCH 28/39] removed all the unnecessary println --- .../renderer/WildMagic/VOI/VOILatticeManagerInterface.java | 1 - .../worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/gov/nih/mipav/view/renderer/WildMagic/VOI/VOILatticeManagerInterface.java b/src/gov/nih/mipav/view/renderer/WildMagic/VOI/VOILatticeManagerInterface.java index 5c3cc9de37..c082dfe85a 100644 --- a/src/gov/nih/mipav/view/renderer/WildMagic/VOI/VOILatticeManagerInterface.java +++ b/src/gov/nih/mipav/view/renderer/WildMagic/VOI/VOILatticeManagerInterface.java @@ -1160,7 +1160,6 @@ public void addAccurateModeListener(AccurateModeListener listener) { public void keyReleased(KeyEvent e) { isShiftSelected = e.isShiftDown(); movingPickedPoint = false; - System.out.println(e.getKeyChar()); // Specific actions based on key codes, e.g., toggle accurate mode on 'M' key if (e.getKeyCode() == KeyEvent.VK_M) { diff --git a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java index 5cf9994e81..f18933aac9 100644 --- a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java +++ b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java @@ -2308,8 +2308,7 @@ private void update3DModel(Vector3f point) { public Vector3f interpolate(int t0, int t1, float tq) { Vector3f pt0 = chart3DPoints.get(t0); Vector3f pt1 = chart3DPoints.get(t1); - System.out.println("pt0: " + pt0); - System.out.println("pt1: " + pt1); + float m0 = (t1 - tq) / (t1 - t0); float m1 = (tq - t0) / (t1 - t0); From 6bcea5946f2d32c9ccd4aca9f0e4cb05c49c9c20 Mon Sep 17 00:00:00 2001 From: chend Date: Wed, 5 Jun 2024 16:44:39 -0400 Subject: [PATCH 29/39] changed mkdir into mkdirs to fix the annotation failed to save issue --- .../WormUntwisting/LatticeModel.java | 74 +++++++++---------- 1 file changed, 35 insertions(+), 39 deletions(-) diff --git a/src/gov/nih/mipav/view/renderer/WildMagic/WormUntwisting/LatticeModel.java b/src/gov/nih/mipav/view/renderer/WildMagic/WormUntwisting/LatticeModel.java index 5e0db20054..930f70d45d 100644 --- a/src/gov/nih/mipav/view/renderer/WildMagic/WormUntwisting/LatticeModel.java +++ b/src/gov/nih/mipav/view/renderer/WildMagic/WormUntwisting/LatticeModel.java @@ -82,7 +82,7 @@ public static void checkParentDir( String parentDir ) } else if (parentFileDir.exists() && !parentFileDir.isDirectory()) { // do nothing } else { // voiFileDir does not exist // System.err.println( "LatticeModel:checkParentDir" + parentDir); - parentFileDir.mkdir(); + parentFileDir.mkdirs(); } } @@ -374,7 +374,7 @@ public static void saveAllVOIsTo(final String voiDir, final ModelImage image) { voiFileDir.delete(); } else { // voiFileDir does not exist // System.err.println( "saveAllVOIsTo " + voiDir); - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } final int nVOI = VOIs.size(); @@ -445,10 +445,8 @@ public static void saveAnnotationsAsCSV(final String dir, final String fileName, // check files, create new directories and delete any existing files: final File fileDir = new File(dir); - if (fileDir.exists() && fileDir.isDirectory()) {} - else if (fileDir.exists() && !fileDir.isDirectory()) { // voiFileDir.delete(); - } else { // voiFileDir does not exist - fileDir.mkdir(); + if (!fileDir.exists() && !fileDir.mkdirs()) { // voiFileDir does not exist + System.err.println("Failed to create directory in svaeAnnotationAsCSV: " + fileDir); } File file = new File(fileDir + File.separator + fileName); @@ -2815,8 +2813,8 @@ private void saveMeshContoursCSV() { String dir = sharedOutputDir + File.separator + "model_contours" + File.separator; File fileDir = new File(dir); - if ( !fileDir.exists() ) { // voiFileDir does not exist - fileDir.mkdir(); + if (!fileDir.exists() && !fileDir.mkdirs()) { // voiFileDir does not exist + System.err.println("Failed to create directory in saveMeshContourCSV: " + fileDir); } File file = new File(dir + File.separator + "wormContours.csv"); @@ -3075,10 +3073,8 @@ public ModelImage segmentLattice(final ModelImage image, boolean saveContourImag final File lrFile = new File(voiDir + list[i]); lrFile.delete(); } - } else if (voiFileDir.exists() && !voiFileDir.isDirectory()) { // voiFileDir.delete(); - } else { // voiFileDir does not exist - // System.err.println( "segmentLattice" + voiDir); - voiFileDir.mkdir(); + } else if (!voiFileDir.exists() && !voiFileDir.mkdirs()) { // voiFileDir does not exist + System.err.println("Failed to create directory in segmentLattice: " + voiFileDir); } System.err.println( "Segment Lattice " + paddingFactor ); @@ -3734,17 +3730,17 @@ public void setImage(ModelImage image) outputDirectory = new String(imageA.getImageDirectory() + JDialogBase.makeImageName(imageA.getImageFileName(), "") + File.separator); boolean isStraight = image.getImageFileName().contains("_straight"); File file = new File(outputDirectory); - if ( !file.exists() && !isStraight ) file.mkdir(); + if ( !file.exists() && !isStraight ) file.mkdirs(); outputDirectory += JDialogBase.makeImageName(imageA.getImageFileName(), "_results") + File.separator; file = new File(outputDirectory); - if ( !file.exists() && !isStraight ) file.mkdir(); + if ( !file.exists() && !isStraight ) file.mkdirs(); } } public void setSharedDirectory( String dir ) { sharedOutputDir = new String(dir); File file = new File(sharedOutputDir + File.separator + "output_images" + File.separator); - if ( !file.exists() ) file.mkdir(); + if ( !file.exists() ) file.mkdirs(); } @@ -5341,14 +5337,14 @@ protected VOI saveAnnotationStatistics(final String imageDir, final ModelImage m if (voiFileDir.exists() && voiFileDir.isDirectory()) { // do nothing } else if (voiFileDir.exists() && !voiFileDir.isDirectory()) { // voiFileDir.delete(); } else { // voiFileDir does not exist - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } voiDir = imageDir + "statistics" + File.separator; voiFileDir = new File(voiDir); if (voiFileDir.exists() && voiFileDir.isDirectory()) {} else if (voiFileDir.exists() && !voiFileDir.isDirectory()) {} else { // voiFileDir - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } File file = new File(voiDir + "AnnotationInfo" + postFix + ".csv"); @@ -5404,7 +5400,7 @@ protected void saveDiameters( Vector diameters, String imageDir ) { } else if (voiFileDir.exists() && !voiFileDir.isDirectory()) { // voiFileDir.delete(); } else { // voiFileDir does not exist // System.err.println( "saveDiameters " + voiDir); - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } voiDir = imageDir + "statistics" + File.separator; @@ -5412,7 +5408,7 @@ protected void saveDiameters( Vector diameters, String imageDir ) { if (voiFileDir.exists() && voiFileDir.isDirectory()) { } else if (voiFileDir.exists() && !voiFileDir.isDirectory()) {} else { // voiFileDir does not exist // System.err.println( "saveDiameters " + voiDir); - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } File file = new File(voiDir + "Diameters.csv"); @@ -5460,14 +5456,14 @@ protected void saveLatticeStatistics( String imageDir, final float length, final if (voiFileDir.exists() && voiFileDir.isDirectory()) { // do nothing } else if (voiFileDir.exists() && !voiFileDir.isDirectory()) { // voiFileDir.delete(); } else { // voiFileDir does not exist - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } voiDir = imageDir + "statistics" + File.separator; voiFileDir = new File(voiDir); if (voiFileDir.exists() && voiFileDir.isDirectory()) { } else if (voiFileDir.exists() && !voiFileDir.isDirectory()) {} else { // voiFileDir does not exist - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } File file = new File(voiDir + "LatticeInfo" + postFix + ".csv"); @@ -5508,7 +5504,7 @@ protected void savePositions( VOIContour contour, String imageDir, String name ) } else if (voiFileDir.exists() && !voiFileDir.isDirectory()) { // voiFileDir.delete(); } else { // voiFileDir does not exist // System.err.println( "savePositions " + voiDir); - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } voiDir = imageDir + "statistics" + File.separator; @@ -5516,7 +5512,7 @@ protected void savePositions( VOIContour contour, String imageDir, String name ) if (voiFileDir.exists() && voiFileDir.isDirectory()) { } else if (voiFileDir.exists() && !voiFileDir.isDirectory()) {} else { // voiFileDir does not exist // System.err.println( "savePositions " + voiDir); - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } File file = new File(voiDir + name + "Positions.csv"); @@ -5558,7 +5554,7 @@ protected void saveSamplePlanes( VOI planes, String imageDir ) { } else if (voiFileDir.exists() && !voiFileDir.isDirectory()) { // voiFileDir.delete(); } else { // voiFileDir does not exist // System.err.println( "saveSamplePlanes " + voiDir); - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } voiDir = imageDir + "statistics" + File.separator; @@ -5566,7 +5562,7 @@ protected void saveSamplePlanes( VOI planes, String imageDir ) { if (voiFileDir.exists() && voiFileDir.isDirectory()) { } else if (voiFileDir.exists() && !voiFileDir.isDirectory()) {} else { // voiFileDir does not exist // System.err.println( "saveSamplePlanes " + voiDir); - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } File file = new File(voiDir + "SamplePlanes.csv"); @@ -6654,13 +6650,13 @@ private void saveImage(final String imageName, final ModelImage image, final boo } else if (voiFileDir.exists() && !voiFileDir.isDirectory()) { // voiFileDir.delete(); } else { // voiFileDir does not exist // System.err.println( "saveImage " + voiDir); - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } voiDir = outputDirectory + File.separator + "output_images" + File.separator; voiFileDir = new File(voiDir); if (voiFileDir.exists() && voiFileDir.isDirectory()) {} else if (voiFileDir.exists() && !voiFileDir.isDirectory()) {} else { // voiFileDir - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } final File file = new File(voiDir + imageName); @@ -6688,7 +6684,7 @@ public static void saveImage(final ModelImage originalImage, final ModelImage im } else if (voiFileDir.exists() && !voiFileDir.isDirectory()) { // voiFileDir.delete(); } else { // voiFileDir does not exist // System.err.println( "saveImage " + voiDir); - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } voiDir = outputDirectory + File.separator + subDir + File.separator; @@ -6729,7 +6725,7 @@ public static void saveImage(final ModelImage originalImage, final ModelImage im // } else if (voiFileDir.exists() && !voiFileDir.isDirectory()) { // voiFileDir.delete(); // } else { // voiFileDir does not exist // // System.err.println( "saveImage " + voiDir); -// voiFileDir.mkdir(); +// voiFileDir.mkdirs(); // } // // maxVal++; @@ -6785,7 +6781,7 @@ public static void saveContourAsCSV( ModelImage image, String subDir, String pos } else if (voiFileDir.exists() && !voiFileDir.isDirectory()) { // voiFileDir.delete(); } else { // voiFileDir does not exist // System.err.println( "saveImage " + voiDir); - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } voiDir = outputDirectory + File.separator + subDir + File.separator; @@ -6805,7 +6801,7 @@ public static void saveContourAsCSV( final String voiDir, final String fileName, if (fileDir.exists() && fileDir.isDirectory()) {} else if (fileDir.exists() && !fileDir.isDirectory()) { // voiFileDir.delete(); } else { // voiFileDir does not exist - fileDir.mkdir(); + fileDir.mkdirs(); } File file = new File(fileDir + File.separator + fileName); if (file.exists()) { @@ -6901,7 +6897,7 @@ public static void saveBasisVectorsAsCSV( ModelImage image, String subDir, Strin } else if (voiFileDir.exists() && !voiFileDir.isDirectory()) { // voiFileDir.delete(); } else { // voiFileDir does not exist // System.err.println( "saveImage " + voiDir); - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } voiDir = outputDirectory + File.separator + subDir + File.separator; @@ -6914,7 +6910,7 @@ public static void saveBasisVectorsAsCSV( ModelImage image, String subDir, Strin if (fileDir.exists() && fileDir.isDirectory()) {} else if (fileDir.exists() && !fileDir.isDirectory()) { // voiFileDir.delete(); } else { // voiFileDir does not exist - fileDir.mkdir(); + fileDir.mkdirs(); } File file = new File(fileDir + File.separator + imageName); if (file.exists()) { @@ -6956,7 +6952,7 @@ private void saveContours( ModelImage image, Box3f[] contours ) { if (voiFileDir.exists() && voiFileDir.isDirectory()) { // do nothing } else if (voiFileDir.exists() && !voiFileDir.isDirectory()) { // voiFileDir.delete(); } else { // voiFileDir does not exist - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } String imageName = JDialogBase.makeImageName(image.getImageFileName(), ""); imageName = imageName + "_contours"; @@ -7080,7 +7076,7 @@ public static void saveTriMesh( ModelImage image, String outputDirectory, String } else if (voiFileDir.exists() && !voiFileDir.isDirectory()) { // voiFileDir.delete(); } else { // voiFileDir does not exist // System.err.println( "saveImage " + voiDir); - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } voiDir = outputDirectory + File.separator + subDir + File.separator; voiFileDir = new File(voiDir); @@ -7088,7 +7084,7 @@ public static void saveTriMesh( ModelImage image, String outputDirectory, String } else if (voiFileDir.exists() && !voiFileDir.isDirectory()) { // voiFileDir.delete(); } else { // voiFileDir does not exist // System.err.println( "saveImage " + voiDir); - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } String imageName = JDialogBase.makeImageName(image.getImageFileName(), ""); @@ -7130,7 +7126,7 @@ private void saveSpline(String outputDirectory, VOI data, Vector3f transformedOr } else if (voiFileDir.exists() && !voiFileDir.isDirectory()) { // voiFileDir.delete(); } else { // voiFileDir does not exist // System.err.println( "saveSpline " + voiDir); - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } voiDir = outputDirectory + File.separator + "statistics" + File.separator; voiFileDir = new File(voiDir); @@ -7139,7 +7135,7 @@ private void saveSpline(String outputDirectory, VOI data, Vector3f transformedOr // not // exist // System.err.println( "saveSpline " + voiDir); - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } File file = new File(voiDir + data.getName() + postFix + ".csv"); @@ -7312,7 +7308,7 @@ private void untwistLattice(final ModelImage image, final int[] resultExtents) if (voiFileDir.exists() && voiFileDir.isDirectory()) { } else if (voiFileDir.exists() && !voiFileDir.isDirectory()) {} else { // voiFileDir does not exist // System.err.println( "untwistLattice" + voiDir); - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } File file = new File(voiDir + imageName + "_Frame_Straight.csv"); From 3408bbc8c02e1658518ed7551938311e3c7e559f Mon Sep 17 00:00:00 2001 From: chend Date: Wed, 5 Jun 2024 17:27:34 -0400 Subject: [PATCH 30/39] fixed the mkdir if statement to consider the case when !dir.mkdirs() failed --- .../WildMagic/WormUntwisting/WormData.java | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/gov/nih/mipav/view/renderer/WildMagic/WormUntwisting/WormData.java b/src/gov/nih/mipav/view/renderer/WildMagic/WormUntwisting/WormData.java index 7e730d6315..85dc3f9919 100644 --- a/src/gov/nih/mipav/view/renderer/WildMagic/WormUntwisting/WormData.java +++ b/src/gov/nih/mipav/view/renderer/WildMagic/WormUntwisting/WormData.java @@ -87,17 +87,15 @@ public WormData( ModelImage image ) checkParentDir(parentDir); } File dir = new File(outputDirectory); - if ( !dir.exists() ) - { - // System.err.println( "WormData " + outputDirectory); - dir.mkdir(); - } + + if (!dir.exists() && !dir.mkdirs()) { // dir does not exist + System.err.println("Failed to create directory in WormData: " + dir); + } + if ( outputImagesDirectory != null ) { dir = new File(outputImagesDirectory); - if ( !dir.exists() ) - { - // System.err.println( "WormData " + outputImagesDirectory); - dir.mkdir(); + if ( !dir.exists() && !dir.mkdirs()){ + System.err.println("Failed to create directory in WormData: " + dir); } } } From 61c775c4083b16051bc1f170d8df5640d140986d Mon Sep 17 00:00:00 2001 From: chend Date: Fri, 7 Jun 2024 15:34:16 -0400 Subject: [PATCH 31/39] created CustomChartPanel to separate the chartPanel related methods from PluglnDialogVolumeRenderDualJanelia --- .../worm/untwisting/CustomChartPanel.java | 201 ++++++++++++++++++ .../PlugInDialogVolumeRenderDualJanelia.java | 199 ++--------------- 2 files changed, 219 insertions(+), 181 deletions(-) create mode 100644 src/org/janelia/mipav/plugins/worm/untwisting/CustomChartPanel.java diff --git a/src/org/janelia/mipav/plugins/worm/untwisting/CustomChartPanel.java b/src/org/janelia/mipav/plugins/worm/untwisting/CustomChartPanel.java new file mode 100644 index 0000000000..6347faca4d --- /dev/null +++ b/src/org/janelia/mipav/plugins/worm/untwisting/CustomChartPanel.java @@ -0,0 +1,201 @@ +package org.janelia.mipav.plugins.worm.untwisting; + +import org.jfree.chart.*; +import org.jfree.chart.axis.ValueAxis; +import org.jfree.chart.event.MarkerChangeEvent; +import org.jfree.chart.event.MarkerChangeListener; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.chart.plot.ValueMarker; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; +import org.jfree.chart.ui.RectangleAnchor; +import org.jfree.chart.ui.TextAnchor; +import org.jfree.chart.ui.Layer; +import org.jfree.data.xy.XYSeries; +import org.jfree.data.xy.XYSeriesCollection; + +import WildMagic.LibFoundation.Mathematics.Vector3f; + +import java.awt.*; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.geom.Rectangle2D; +import java.util.List; + +public class CustomChartPanel extends ChartPanel implements MarkerChangeListener { + /** + * + */ + private static final long serialVersionUID = 1L; + private JFreeChart selectionChart; + private List chart3DPoints; + private PlugInDialogVolumeRenderDualJanelia parent; + + public CustomChartPanel(List values, String title, PlugInDialogVolumeRenderDualJanelia parent) { + super(null); + this.selectionChart = createChart(values, title, this); + this.parent = parent; + this.setChart(selectionChart); + initialize(); + + } + + private void initialize() { + setPreferredSize(new Dimension(600, 300)); + setMouseWheelEnabled(false); + setDomainZoomable(false); + setRangeZoomable(false); + setMouseZoomable(false, false); + setFillZoomRectangle(false); + setZoomAroundAnchor(false); + + // Add a mouse motion listener to handle dragging movements over the chart + addMouseMotionListener(new MouseAdapter() { + @Override + public void mouseDragged(MouseEvent e) { + // Get the plot area for accurate coordinate calculation + Rectangle2D plotArea = getScreenDataArea(); + XYPlot plot = selectionChart.getXYPlot(); + ValueAxis xAxis = plot.getDomainAxis(); + double x = xAxis.java2DToValue(e.getX(), plotArea, plot.getDomainAxisEdge()); + + // Determine the corresponding Y-value by finding the nearest index + XYSeriesCollection dataset = (XYSeriesCollection) plot.getDataset(); + XYSeries series = dataset.getSeries(0); + + int index = findNearestXIndex(series, x); + double y = series.getY(index).doubleValue(); + + // Update the marker's value and label on the chart + ValueMarker marker = (ValueMarker) plot.getDomainMarkers(Layer.FOREGROUND).iterator().next(); + marker.setValue(x); + marker.setLabel(String.format("Value: %.2f", y)); + + // Redraw the chart to reflect changes + repaint(); + e.consume(); + } + }); + } + + /** + * Finds the index of the closest X value to the given target x. + * + * @param series The series of data points. + * @param x The target x value to match. + * @return The index of the closest x value. + */ + private int findNearestXIndex(XYSeries series, double x) { + double minDistance = Double.MAX_VALUE; + int nearestIndex = -1; + + for (int i = 0; i < series.getItemCount(); i++) { + double distance = Math.abs(series.getX(i).doubleValue() - x); + if (distance < minDistance) { + minDistance = distance; + nearestIndex = i; + } + } + return nearestIndex; + } + + public void updateChart(List values, String title) { + this.selectionChart = createChart(values, title, this); + setChart(this.selectionChart); + revalidate(); + repaint(); + } + + // Set up a marker change listener to handle marker position changes + @Override + public void markerChanged(MarkerChangeEvent event) { + ValueMarker marker = (ValueMarker) event.getMarker(); + float tq = (float) marker.getValue(); + // Calculate the indices for interpolation + int t0 = (int) Math.floor(tq); + int t1 = (int) Math.ceil(tq); + + if (t0 >= 0 && t1 < chart3DPoints.size() && t0 != t1) { + Vector3f interpolatedPoint = interpolate(t0, t1, tq); + parent.update3DModel(interpolatedPoint); + } else if (t0 == t1 && t0 < chart3DPoints.size()) { + Vector3f exactPoint = chart3DPoints.get(t0); + parent.update3DModel(exactPoint); + } + } + + /** + * Interpolates between two 3D points based on a given interpolation parameter. + * + * @param t0 Index of the first point. + * @param t1 Index of the second point. + * @param tq The interpolation parameter, typically derived from the marker's + * position. + * @return The interpolated 3D point. + */ + private Vector3f interpolate(int t0, int t1, float tq) { + Vector3f pt0 = chart3DPoints.get(t0); + Vector3f pt1 = chart3DPoints.get(t1); + + float m0 = (t1 - tq) / (t1 - t0); + float m1 = (tq - t0) / (t1 - t0); + + float x = (float) (m0 * pt0.X + m1 * pt1.X); + float y = (float) (m0 * pt0.Y + m1 * pt1.Y); + float z = (float) (m0 * pt0.Z + m1 * pt1.Z); + + return new Vector3f(x, y, z); + } + + /** + * Creates a chart using a list of values and assigns it a title. Each value in + * the list is plotted against its index. + * + * @param values List of floating-point values for the Y-axis. + * @param title Title of the chart. + * @return A JFreeChart object fully initialized. + */ + private static JFreeChart createChart(List values, String title, MarkerChangeListener markerListener) { + XYSeries series = new XYSeries("Data"); + float maxValue = -Float.MAX_VALUE; + int maxIndex = -1; + + // Populate the series with values and track the maximum value and its index + for (int i = 0; i < values.size(); i++) { + float value = values.get(i); + series.add(i, value); + if (value > maxValue) { + maxValue = value; + maxIndex = i; + } + } + + XYSeriesCollection dataset = new XYSeriesCollection(); + dataset.addSeries(series); + + // Create the chart + JFreeChart chart = ChartFactory.createXYLineChart(title, "Index", "Value", dataset, PlotOrientation.VERTICAL, + true, true, false); + + XYPlot plot = chart.getXYPlot(); + XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(); + renderer.setSeriesPaint(0, Color.BLUE); + renderer.setSeriesStroke(0, new BasicStroke(2.0f)); + plot.setRenderer(renderer); + + ValueMarker marker = new ValueMarker(maxIndex); + marker.setPaint(Color.RED); + marker.setLabel("Max Value: " + maxValue); + marker.setLabelAnchor(RectangleAnchor.CENTER); + marker.setLabelTextAnchor(TextAnchor.CENTER_LEFT); + plot.addDomainMarker(marker); + + marker.addChangeListener(markerListener); + + return chart; + } + + public void setChart3DPoints(List points) { + this.chart3DPoints = points; + } +} diff --git a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java index f18933aac9..28bd37e89e 100644 --- a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java +++ b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java @@ -286,9 +286,8 @@ public class PlugInDialogVolumeRenderDualJanelia extends JFrame private JSplitPane latticePanel = null; private JPanel latticePanelSingle = null; - private JFreeChart selectionChart; - private ChartPanel chartPanel; - private List chart3DPoints; + private CustomChartPanel chartPanel; + private class IntegratedWormData { private VOIVector annotations; @@ -2220,111 +2219,6 @@ private void annotationAnimationFromSpreadSheet() { progress = null; } - /** - * Creates a chart using a list of values and assigns it a title. Each value in - * the list is plotted against its index. - * - * @param values List of floating-point values for the Y-axis. - * @param title Title of the chart. - * @return A JFreeChart object fully initialized. - */ - public JFreeChart createChart(List values, String title) { - XYSeries series = new XYSeries("Data"); - float maxValue = -Float.MAX_VALUE; - int maxIndex = -1; - - // Populate the series with values and track the maximum value and its index - for (int i = 0; i < values.size(); i++) { - float value = values.get(i); - series.add(i, value); - if (value > maxValue) { - maxValue = value; - maxIndex = i; - } - } - - XYSeriesCollection dataset = new XYSeriesCollection(); - dataset.addSeries(series); - - // Create the chart - JFreeChart chart = ChartFactory.createXYLineChart(title, "Index", "Value", dataset, PlotOrientation.VERTICAL, - true, true, false); - - XYPlot plot = chart.getXYPlot(); - XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(); - renderer.setSeriesPaint(0, Color.BLUE); - renderer.setSeriesStroke(0, new BasicStroke(2.0f)); - plot.setRenderer(renderer); - - // Add a dynamic marker at the position of the maximum value - ValueMarker marker = new ValueMarker(maxIndex); - marker.setPaint(Color.RED); - marker.setLabel("Max Value: " + maxValue); - marker.setLabelAnchor(RectangleAnchor.CENTER); - marker.setLabelTextAnchor(TextAnchor.CENTER_LEFT); - plot.addDomainMarker(marker); - - // Set up a marker change listener to handle marker position changes - marker.addChangeListener(new MarkerChangeListener() { - @Override - public void markerChanged(MarkerChangeEvent event) { - ValueMarker marker = (ValueMarker) event.getMarker(); - - float tq = (float) marker.getValue(); - - // Calculate the indices for interpolation - int t0 = (int) Math.floor(tq); - int t1 = (int) Math.ceil(tq); - - if (t0 >= 0 && t1 < chart3DPoints.size() && t0 != t1) { - - Vector3f interpolatedPoint = interpolate(t0, t1, tq); - update3DModel(interpolatedPoint); - - } else if (t0 == t1 && t0 < chart3DPoints.size()) { // if t0 == t1 - Vector3f exactPoint = chart3DPoints.get(t0); - update3DModel(exactPoint); - } - } - - /** - * Updates the 3D model visualization based on a new 3D point. - * - * @param point The 3D point that the model needs to reflect. - */ - private void update3DModel(Vector3f point) { - activeImage.voiManager.modify3DMarker(point, point, point); - } - - /** - * Interpolates between two 3D points based on a given interpolation parameter. - * - * @param t0 Index of the first point. - * @param t1 Index of the second point. - * @param tq The interpolation parameter, typically derived from the marker's - * position. - * @return The interpolated 3D point. - */ - public Vector3f interpolate(int t0, int t1, float tq) { - Vector3f pt0 = chart3DPoints.get(t0); - Vector3f pt1 = chart3DPoints.get(t1); - - float m0 = (t1 - tq) / (t1 - t0); - float m1 = (tq - t0) / (t1 - t0); - - float x = (float) (m0 * pt0.X + m1 * pt1.X); - float y = (float) (m0 * pt0.Y + m1 * pt1.Y); - float z = (float) (m0 * pt0.Z + m1 * pt1.Z); - - Vector3f ptq = new Vector3f(x, y, z); - - return ptq; - } - }); - - return chart; - } - /** * Updates the plot panel by invoking the chart creation and updating the * display. @@ -2333,26 +2227,21 @@ public Vector3f interpolate(int t0, int t1, float tq) { * @param values List of values corresponding to each 3D point. * @param title Title for the updated plot. */ - public void updatePlotPanel(List points, List values, String title) { - SwingUtilities.invokeLater(() -> { - selectionChart = createChart(values, title); - chartPanel.setChart(selectionChart); - - chartPanel.setPreferredSize(new Dimension(600, 400)); - chartPanel.setMouseWheelEnabled(true); - - chartPanel.setDomainZoomable(false); - chartPanel.setRangeZoomable(false); - chartPanel.setMouseZoomable(false, false); - chartPanel.setFillZoomRectangle(false); - chartPanel.setZoomAroundAnchor(false); - - chart3DPoints = points; - - chartPanel.revalidate(); - chartPanel.repaint(); - }); - } + public void updatePlotPanel(List points, List values, String title) { + SwingUtilities.invokeLater(() -> { + chartPanel.updateChart(values, title); + chartPanel.setChart3DPoints(points); + }); + } + + /** + * Updates the 3D model visualization based on a new 3D point. + * + * @param point The 3D point that the model needs to reflect. + */ + public void update3DModel(Vector3f point) { + activeImage.voiManager.modify3DMarker(point, point, point); + } /** * User-interface initialization. If the UI is integrated all panels are @@ -2479,59 +2368,7 @@ public void actionPerformed(ActionEvent e) { buttonPanel.add(accurateModeButton); accuratePanel.add(buttonPanel, BorderLayout.NORTH); - // Create a chart - selectionChart = createChart(Arrays.asList(1.0f, 2.0f, 3.0f), "Chart"); - chartPanel = new ChartPanel(selectionChart); - - // Add a mouse motion listener to handle dragging movements over the chart - chartPanel.addMouseMotionListener(new MouseAdapter() { - @Override - public void mouseDragged(MouseEvent e) { - // Get the plot area for accurate coordinate calculation - Rectangle2D plotArea = chartPanel.getScreenDataArea(); - XYPlot plot = selectionChart.getXYPlot(); - ValueAxis xAxis = plot.getDomainAxis(); - double x = xAxis.java2DToValue(e.getX(), plotArea, plot.getDomainAxisEdge()); - - // Determine the corresponding Y-value by finding the nearest index - XYSeriesCollection dataset = (XYSeriesCollection) plot.getDataset(); - XYSeries series = dataset.getSeries(0); - - int index = findNearestXIndex(series, x); - double y = series.getY(index).doubleValue(); - - // Update the marker's value and label on the chart - ValueMarker marker = (ValueMarker) plot.getDomainMarkers(Layer.FOREGROUND).iterator().next(); - marker.setValue(x); - marker.setLabel(String.format("Value: %.2f", y)); - - // Redraw the chart to reflect changes - chartPanel.repaint(); - e.consume(); - } - - /** - * Finds the index of the closest X value to the given target x. - * - * @param series The series of data points. - * @param x The target x value to match. - * @return The index of the closest x value. - */ - private int findNearestXIndex(XYSeries series, double x) { - double minDistance = Double.MAX_VALUE; - int nearestIndex = -1; - - for (int i = 0; i < series.getItemCount(); i++) { - double distance = Math.abs(series.getX(i).doubleValue() - x); - if (distance < minDistance) { - minDistance = distance; - nearestIndex = i; - } - } - return nearestIndex; - } - }); - + chartPanel = new CustomChartPanel(Arrays.asList(1.0f, 2.0f, 3.0f), "Chart", this); accuratePanel.add(chartPanel, BorderLayout.CENTER); tabbedPane = new JTabbedPane(); From 6726926fd6a4ecc0de1c1d1af0221ebc87837c3a Mon Sep 17 00:00:00 2001 From: chend Date: Fri, 7 Jun 2024 16:25:43 -0400 Subject: [PATCH 32/39] fixed the zoomed in issue by created a new method setChart() --- .../worm/untwisting/CustomChartPanel.java | 11 +++++++ .../PlugInDialogVolumeRenderDualJanelia.java | 33 ++++++++++--------- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/src/org/janelia/mipav/plugins/worm/untwisting/CustomChartPanel.java b/src/org/janelia/mipav/plugins/worm/untwisting/CustomChartPanel.java index 6347faca4d..d4eb2984d4 100644 --- a/src/org/janelia/mipav/plugins/worm/untwisting/CustomChartPanel.java +++ b/src/org/janelia/mipav/plugins/worm/untwisting/CustomChartPanel.java @@ -48,6 +48,7 @@ private void initialize() { setMouseZoomable(false, false); setFillZoomRectangle(false); setZoomAroundAnchor(false); + // Add a mouse motion listener to handle dragging movements over the chart addMouseMotionListener(new MouseAdapter() { @@ -77,6 +78,16 @@ public void mouseDragged(MouseEvent e) { } }); } + + public void setChart(JFreeChart chart) { + super.setChart(chart); + setMouseWheelEnabled(false); + setDomainZoomable(false); + setRangeZoomable(false); + setMouseZoomable(false, false); + setFillZoomRectangle(false); + setZoomAroundAnchor(false); + } /** * Finds the index of the closest X value to the given target x. diff --git a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java index 28bd37e89e..2d9ccda807 100644 --- a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java +++ b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java @@ -2227,21 +2227,24 @@ private void annotationAnimationFromSpreadSheet() { * @param values List of values corresponding to each 3D point. * @param title Title for the updated plot. */ - public void updatePlotPanel(List points, List values, String title) { - SwingUtilities.invokeLater(() -> { - chartPanel.updateChart(values, title); - chartPanel.setChart3DPoints(points); - }); - } - - /** - * Updates the 3D model visualization based on a new 3D point. - * - * @param point The 3D point that the model needs to reflect. - */ - public void update3DModel(Vector3f point) { - activeImage.voiManager.modify3DMarker(point, point, point); - } + public void updatePlotPanel(List points, List values, String title) { + SwingUtilities.invokeLater(() -> { + chartPanel.updateChart(values, title); + chartPanel.setChart3DPoints(points); + + chartPanel.revalidate(); + chartPanel.repaint(); + }); + } + + /** + * Updates the 3D model visualization based on a new 3D point. + * + * @param point The 3D point that the model needs to reflect. + */ + public void update3DModel(Vector3f point) { + activeImage.voiManager.modify3DMarker(point, point, point); + } /** * User-interface initialization. If the UI is integrated all panels are From 27eedeac5d960635884ee1db39ecf63edd87a922 Mon Sep 17 00:00:00 2001 From: chend Date: Tue, 11 Jun 2024 10:36:16 -0400 Subject: [PATCH 33/39] Created a fakePanel to split the panel into desired layout and fixed the title --- .../WildMagic/VolumeTriPlanarRender.java | 4 ++-- .../worm/untwisting/CustomChartPanel.java | 4 ++-- .../PlugInDialogVolumeRenderDualJanelia.java | 16 +++++++++++++++- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java b/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java index 36b2df8a8f..c0a1a111a2 100644 --- a/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java +++ b/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java @@ -1064,9 +1064,9 @@ else if ( !doAutomaticLabels() ) } } if (m_kVOIInterface.isAccurateMode()) { - notifyPlotListeners(points, plotAccurateValues, "Accurate Values"); + notifyPlotListeners(points, plotAccurateValues, "Chart"); } else { - notifyPlotListeners(points, plotValues, "3-Color Values"); + notifyPlotListeners(points, plotValues, "3-Color Chart"); } } diff --git a/src/org/janelia/mipav/plugins/worm/untwisting/CustomChartPanel.java b/src/org/janelia/mipav/plugins/worm/untwisting/CustomChartPanel.java index d4eb2984d4..3ccaf1f848 100644 --- a/src/org/janelia/mipav/plugins/worm/untwisting/CustomChartPanel.java +++ b/src/org/janelia/mipav/plugins/worm/untwisting/CustomChartPanel.java @@ -36,12 +36,12 @@ public CustomChartPanel(List values, String title, PlugInDialogVolumeRend this.selectionChart = createChart(values, title, this); this.parent = parent; this.setChart(selectionChart); + setPreferredSize(new Dimension(100,100)); initialize(); } - + private void initialize() { - setPreferredSize(new Dimension(600, 300)); setMouseWheelEnabled(false); setDomainZoomable(false); setRangeZoomable(false); diff --git a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java index 2d9ccda807..4151f840cd 100644 --- a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java +++ b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java @@ -106,6 +106,7 @@ import java.util.Vector; import java.util.stream.Collectors; +import javax.swing.BoxLayout; import javax.swing.ButtonGroup; import javax.swing.JButton; import javax.swing.JCheckBox; @@ -2350,7 +2351,10 @@ private void init() { // Create a panel for toggling between accurate and 3-color modes(not working // currently) accuratePanel = new JPanel(new BorderLayout()); + accuratePanel.setPreferredSize(new Dimension(400, 400)); + accuratePanel.setMaximumSize(new Dimension(400, 400)); JPanel buttonPanel = new JPanel(); + // Initialize a toggle button for switching modes accurateModeButton = new JToggleButton("Accurate Mode"); accurateModeChanged(true); @@ -2372,7 +2376,17 @@ public void actionPerformed(ActionEvent e) { accuratePanel.add(buttonPanel, BorderLayout.NORTH); chartPanel = new CustomChartPanel(Arrays.asList(1.0f, 2.0f, 3.0f), "Chart", this); - accuratePanel.add(chartPanel, BorderLayout.CENTER); + + // Created a fakePanel to split the panel + JPanel fakePanel = new JPanel(); + fakePanel.setPreferredSize(new Dimension(100,500)); + JSplitPane chartFakeSplit = new JSplitPane(JSplitPane.VERTICAL_SPLIT, chartPanel, fakePanel); + chartFakeSplit.setOneTouchExpandable(true); + chartFakeSplit.setContinuousLayout(true); + chartFakeSplit.setResizeWeight(0.7); + chartFakeSplit.setDividerLocation(0.5); + + accuratePanel.add(chartFakeSplit, BorderLayout.CENTER); tabbedPane = new JTabbedPane(); tabbedPane.addTab("LUT", null, lutPanel); From 4933d9d5d79985a7e4db9f611156e24015a19b8c Mon Sep 17 00:00:00 2001 From: chend Date: Tue, 11 Jun 2024 15:39:03 -0400 Subject: [PATCH 34/39] changed the plot settings like color and size for the marker and the background. --- .../worm/untwisting/CustomChartPanel.java | 29 +++++++++++++++---- .../PlugInDialogVolumeRenderDualJanelia.java | 1 - 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/org/janelia/mipav/plugins/worm/untwisting/CustomChartPanel.java b/src/org/janelia/mipav/plugins/worm/untwisting/CustomChartPanel.java index 3ccaf1f848..d9568f7387 100644 --- a/src/org/janelia/mipav/plugins/worm/untwisting/CustomChartPanel.java +++ b/src/org/janelia/mipav/plugins/worm/untwisting/CustomChartPanel.java @@ -23,9 +23,13 @@ import java.util.List; public class CustomChartPanel extends ChartPanel implements MarkerChangeListener { - /** - * - */ + + +/** + * CustomChartPanel is a custom extension of the ChartPanel class from JFreeChart + * used to display and interact with a dynamic XY line chart. It includes custom + * behaviors for handling marker changes and mouse drag events. + */ private static final long serialVersionUID = 1L; private JFreeChart selectionChart; private List chart3DPoints; @@ -71,6 +75,9 @@ public void mouseDragged(MouseEvent e) { ValueMarker marker = (ValueMarker) plot.getDomainMarkers(Layer.FOREGROUND).iterator().next(); marker.setValue(x); marker.setLabel(String.format("Value: %.2f", y)); + + marker.setLabelFont(new Font("Serif", Font.BOLD, 14)); + marker.setStroke(new BasicStroke(2.0f)); // Redraw the chart to reflect changes repaint(); @@ -189,18 +196,28 @@ private static JFreeChart createChart(List values, String title, MarkerCh true, true, false); XYPlot plot = chart.getXYPlot(); + plot.setBackgroundPaint(Color.GRAY); + plot.setDomainGridlinePaint(Color.DARK_GRAY); + plot.setRangeGridlinePaint(Color.DARK_GRAY); + XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(); - renderer.setSeriesPaint(0, Color.BLUE); + renderer.setSeriesPaint(0, Color.YELLOW); renderer.setSeriesStroke(0, new BasicStroke(2.0f)); plot.setRenderer(renderer); ValueMarker marker = new ValueMarker(maxIndex); - marker.setPaint(Color.RED); + marker.setPaint(Color.CYAN); marker.setLabel("Max Value: " + maxValue); + + marker.setLabelFont(new Font("Serif", Font.BOLD, 14)); + marker.setLabelPaint(Color.WHITE); + marker.setLabelAnchor(RectangleAnchor.CENTER); marker.setLabelTextAnchor(TextAnchor.CENTER_LEFT); + marker.setStroke(new BasicStroke(2.0f)); plot.addDomainMarker(marker); - + + marker.addChangeListener(markerListener); return chart; diff --git a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java index 4151f840cd..9961badeaa 100644 --- a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java +++ b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java @@ -106,7 +106,6 @@ import java.util.Vector; import java.util.stream.Collectors; -import javax.swing.BoxLayout; import javax.swing.ButtonGroup; import javax.swing.JButton; import javax.swing.JCheckBox; From 1d181027987d2ac2c2e98c74b46ba69723744bb3 Mon Sep 17 00:00:00 2001 From: chend Date: Wed, 12 Jun 2024 17:17:24 -0400 Subject: [PATCH 35/39] Renamed the CustomChartPanel into SelectionChartPanel. --- .../untwisting/PlugInDialogVolumeRenderDualJanelia.java | 4 ++-- .../{CustomChartPanel.java => SelectionChartPanel.java} | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) rename src/org/janelia/mipav/plugins/worm/untwisting/{CustomChartPanel.java => SelectionChartPanel.java} (96%) diff --git a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java index 9961badeaa..afbcaab1dd 100644 --- a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java +++ b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java @@ -286,7 +286,7 @@ public class PlugInDialogVolumeRenderDualJanelia extends JFrame private JSplitPane latticePanel = null; private JPanel latticePanelSingle = null; - private CustomChartPanel chartPanel; + private SelectionChartPanel chartPanel; private class IntegratedWormData { @@ -2374,7 +2374,7 @@ public void actionPerformed(ActionEvent e) { buttonPanel.add(accurateModeButton); accuratePanel.add(buttonPanel, BorderLayout.NORTH); - chartPanel = new CustomChartPanel(Arrays.asList(1.0f, 2.0f, 3.0f), "Chart", this); + chartPanel = new SelectionChartPanel(Arrays.asList(1.0f, 2.0f, 3.0f), "Chart", this); // Created a fakePanel to split the panel JPanel fakePanel = new JPanel(); diff --git a/src/org/janelia/mipav/plugins/worm/untwisting/CustomChartPanel.java b/src/org/janelia/mipav/plugins/worm/untwisting/SelectionChartPanel.java similarity index 96% rename from src/org/janelia/mipav/plugins/worm/untwisting/CustomChartPanel.java rename to src/org/janelia/mipav/plugins/worm/untwisting/SelectionChartPanel.java index d9568f7387..b083850654 100644 --- a/src/org/janelia/mipav/plugins/worm/untwisting/CustomChartPanel.java +++ b/src/org/janelia/mipav/plugins/worm/untwisting/SelectionChartPanel.java @@ -22,7 +22,7 @@ import java.awt.geom.Rectangle2D; import java.util.List; -public class CustomChartPanel extends ChartPanel implements MarkerChangeListener { +public class SelectionChartPanel extends ChartPanel implements MarkerChangeListener { /** @@ -35,7 +35,7 @@ public class CustomChartPanel extends ChartPanel implements MarkerChangeListener private List chart3DPoints; private PlugInDialogVolumeRenderDualJanelia parent; - public CustomChartPanel(List values, String title, PlugInDialogVolumeRenderDualJanelia parent) { + public SelectionChartPanel(List values, String title, PlugInDialogVolumeRenderDualJanelia parent) { super(null); this.selectionChart = createChart(values, title, this); this.parent = parent; @@ -46,7 +46,7 @@ public CustomChartPanel(List values, String title, PlugInDialogVolumeRend } private void initialize() { - setMouseWheelEnabled(false); + setMouseWheelEnabled(false); setDomainZoomable(false); setRangeZoomable(false); setMouseZoomable(false, false); From 2b3fa3b49e1bd7d8d0c3b31ae11d5815c751ab19 Mon Sep 17 00:00:00 2001 From: chend Date: Thu, 13 Jun 2024 11:07:14 -0400 Subject: [PATCH 36/39] Deleted unused import and renamed to "Selection Chart" --- .../PlugInDialogVolumeRenderDualJanelia.java | 23 ++++--------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java index afbcaab1dd..1b5968f6d1 100644 --- a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java +++ b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java @@ -128,22 +128,6 @@ import javax.swing.event.ChangeListener; import javax.swing.ImageIcon; -import org.janelia.mipav.test.Plot; -import org.jfree.chart.ChartFactory; -import org.jfree.chart.ChartPanel; -import org.jfree.chart.JFreeChart; -import org.jfree.chart.axis.ValueAxis; -import org.jfree.chart.event.MarkerChangeEvent; -import org.jfree.chart.event.MarkerChangeListener; -import org.jfree.chart.plot.PlotOrientation; -import org.jfree.chart.plot.ValueMarker; -import org.jfree.chart.plot.XYPlot; -import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; -import org.jfree.data.xy.XYSeries; -import org.jfree.data.xy.XYSeriesCollection; -import org.jfree.chart.ui.Layer; -import org.jfree.chart.ui.RectangleAnchor; -import org.jfree.chart.ui.TextAnchor; import org.jocl.Sizeof; import WildMagic.LibFoundation.Mathematics.Mathf; @@ -2358,6 +2342,7 @@ private void init() { accurateModeButton = new JToggleButton("Accurate Mode"); accurateModeChanged(true); accurateModeButton.setPreferredSize(new Dimension(200, 50)); + accurateModeButton.setToolTipText("Press 'M' to toggle Accurate Mode"); // Add an action response to the button accurateModeButton.addActionListener(new ActionListener() { @@ -2373,8 +2358,8 @@ public void actionPerformed(ActionEvent e) { // accurate mode panel buttonPanel.add(accurateModeButton); accuratePanel.add(buttonPanel, BorderLayout.NORTH); - - chartPanel = new SelectionChartPanel(Arrays.asList(1.0f, 2.0f, 3.0f), "Chart", this); + + chartPanel = new SelectionChartPanel(Arrays.asList(0.0f, 0.0f), "Selection Chart", this); // Created a fakePanel to split the panel JPanel fakePanel = new JPanel(); @@ -2386,7 +2371,7 @@ public void actionPerformed(ActionEvent e) { chartFakeSplit.setDividerLocation(0.5); accuratePanel.add(chartFakeSplit, BorderLayout.CENTER); - + tabbedPane = new JTabbedPane(); tabbedPane.addTab("LUT", null, lutPanel); tabbedPane.addTab("Opacity", null, opacityPanel); From 86b646d033ca7d12bf8f7e6d61331fe2202a4d5c Mon Sep 17 00:00:00 2001 From: chend Date: Thu, 13 Jun 2024 11:09:54 -0400 Subject: [PATCH 37/39] Added a JToolTip to the button to give a hint that there is a keyboard command to change the mode. --- .../worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java index 1b5968f6d1..afbebfe003 100644 --- a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java +++ b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java @@ -2371,6 +2371,10 @@ public void actionPerformed(ActionEvent e) { chartFakeSplit.setDividerLocation(0.5); accuratePanel.add(chartFakeSplit, BorderLayout.CENTER); + + + + tabbedPane = new JTabbedPane(); tabbedPane.addTab("LUT", null, lutPanel); From 96a518a912d1a1d58c9e150a4fa58bc2aa381fa8 Mon Sep 17 00:00:00 2001 From: chend Date: Thu, 13 Jun 2024 11:26:08 -0400 Subject: [PATCH 38/39] added comments to explain the plot size changes once it was removed --- .../untwisting/PlugInDialogVolumeRenderDualJanelia.java | 7 ++----- .../mipav/plugins/worm/untwisting/SelectionChartPanel.java | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java index afbebfe003..7322ebf9db 100644 --- a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java +++ b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java @@ -2334,6 +2334,7 @@ private void init() { // Create a panel for toggling between accurate and 3-color modes(not working // currently) accuratePanel = new JPanel(new BorderLayout()); + // Panel get larger horizontally if remove the two lines below accuratePanel.setPreferredSize(new Dimension(400, 400)); accuratePanel.setMaximumSize(new Dimension(400, 400)); JPanel buttonPanel = new JPanel(); @@ -2363,7 +2364,7 @@ public void actionPerformed(ActionEvent e) { // Created a fakePanel to split the panel JPanel fakePanel = new JPanel(); - fakePanel.setPreferredSize(new Dimension(100,500)); + fakePanel.setPreferredSize(new Dimension(100,500)); // plot gets larger vertically if remove this line JSplitPane chartFakeSplit = new JSplitPane(JSplitPane.VERTICAL_SPLIT, chartPanel, fakePanel); chartFakeSplit.setOneTouchExpandable(true); chartFakeSplit.setContinuousLayout(true); @@ -2372,10 +2373,6 @@ public void actionPerformed(ActionEvent e) { accuratePanel.add(chartFakeSplit, BorderLayout.CENTER); - - - - tabbedPane = new JTabbedPane(); tabbedPane.addTab("LUT", null, lutPanel); tabbedPane.addTab("Opacity", null, opacityPanel); diff --git a/src/org/janelia/mipav/plugins/worm/untwisting/SelectionChartPanel.java b/src/org/janelia/mipav/plugins/worm/untwisting/SelectionChartPanel.java index b083850654..d1d0475fe9 100644 --- a/src/org/janelia/mipav/plugins/worm/untwisting/SelectionChartPanel.java +++ b/src/org/janelia/mipav/plugins/worm/untwisting/SelectionChartPanel.java @@ -40,7 +40,7 @@ public SelectionChartPanel(List values, String title, PlugInDialogVolumeR this.selectionChart = createChart(values, title, this); this.parent = parent; this.setChart(selectionChart); - setPreferredSize(new Dimension(100,100)); + setPreferredSize(new Dimension(100,100));// plot gets larger vertically and horizontally if remove this line initialize(); } From 2783fcb24deaf3ff318bb05dbec3fec8f21d5bf4 Mon Sep 17 00:00:00 2001 From: chend Date: Thu, 13 Jun 2024 11:44:44 -0400 Subject: [PATCH 39/39] changed the plot title from 'Chart' into 'Selection Chart' --- .../mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java b/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java index c0a1a111a2..bc3ce1cbf9 100644 --- a/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java +++ b/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java @@ -1064,7 +1064,7 @@ else if ( !doAutomaticLabels() ) } } if (m_kVOIInterface.isAccurateMode()) { - notifyPlotListeners(points, plotAccurateValues, "Chart"); + notifyPlotListeners(points, plotAccurateValues, "Selection Chart"); } else { notifyPlotListeners(points, plotValues, "3-Color Chart"); }