package edu.stanford.rsl.conrad.opencl.rendering; import java.awt.Dimension; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.FileInputStream; import java.io.IOException; import java.nio.IntBuffer; import java.util.ArrayList; import java.util.Iterator; import java.util.Timer; import java.util.TimerTask; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSlider; import javax.swing.SwingUtilities; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import com.jogamp.opencl.CLImage3d; import com.jogamp.opencl.CLImageFormat; import com.jogamp.opencl.CLImageFormat.ChannelOrder; import com.jogamp.opencl.CLImageFormat.ChannelType; import com.jogamp.opencl.CLMemory.Mem; import edu.stanford.rsl.conrad.utils.ImageUtil; import ij.ImagePlus; import ij.process.ImageProcessor; public class OpenCLVolumeRenderer extends OpenCLTextureRendering { /** * */ private static final long serialVersionUID = 369749150640130620L; private boolean multiFrameMode = false; private ArrayList<CLImage3d<IntBuffer>> clImages; private byte[][] volumes; private int nFrames=1; private int time = 0; public OpenCLVolumeRenderer(byte[] volumeData, int sizeX, int sizeY, int sizeZ, boolean stereoMode) { super(volumeData, sizeX, sizeY, sizeZ, stereoMode); /*transferFunc = new float[] { 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, }; */ volumes = new byte[1][volumeData.length]; volumes[0]=volumeData; clImages = new ArrayList<CLImage3d<IntBuffer>>(1); start(); } private double [] minmax = null; public OpenCLVolumeRenderer(ImagePlus image){ super (new byte[image.getWidth() * image.getHeight() * image.getStackSize() / image.getNFrames()], image.getWidth(), image.getHeight(), image.getStackSize() / image.getNFrames(), false); transferFunc = new float[] { 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, }; minmax = ImageUtil.minAndMaxOfImageProcessor(image); minmax[1] *= 1.05; if (image.getNFrames() == 1) multiFrameMode = false; else multiFrameMode = true; nFrames = image.getNFrames(); // Create the image array clImages = new ArrayList<CLImage3d<IntBuffer>>(image.getNFrames()); // rewrite imagePlus container into nFrames single byte volumes volumes = new byte [image.getNFrames()][volumeSize[2]*volumeSize[1]*volumeSize[0]]; for (int n = 0; n < image.getNFrames(); n++) { for (int k = 0; k < volumeSize[2]; k++) { ImageProcessor img = image.getStack().getProcessor((n*volumeSize[2])+k + 1); for (int i = 0; i < image.getWidth(); i++){ for (int j = 0; j < image.getHeight(); j++){ setVoxel(volumes[n], i,j,k, img.getPixelValue(i, j)); } } } } initialized = false; //System.out.println("init"); frame.setTitle("Volume: " + image.getTitle()); } public void setVoxel(byte [] h_volume, int i, int j, int k, double value){ h_volume[((((int)volumeSize[1] * k) + j) * (int)volumeSize[0]) + i] = (byte) (((value - minmax[0]) / (minmax[1] - minmax[0])) * 256); } /** * Entry point for this sample. * * @param args not used */ public static void main(String args[]) { startSample("Bucky.raw", 32, 32, 32, false); // Other input files may be obtained from http://www.volvis.org //startSample("mri_ventricles.raw", 256, 256, 124, false); //startSample("backpack8.raw", 512, 512, 373, false); //startSample("foot.raw", 256, 256, 256, false); } /** * Starts this sample with the data that is read from the file * with the given name. The data is assumed to have the * specified dimensions. * * @param fileName The name of the volume data file to load * @param sizeX The size of the data set in X direction * @param sizeY The size of the data set in Y direction * @param sizeZ The size of the data set in Z direction * @param stereoMode Whether stereo mode should be used */ private static void startSample( String fileName, final int sizeX, final int sizeY, final int sizeZ, final boolean stereoMode) { // Try to read the specified file byte data[] = null; try { int size = sizeX * sizeY * sizeZ; FileInputStream fis = new FileInputStream(fileName); data = new byte[size]; fis.read(data); fis.close(); } catch (IOException e) { System.err.println("Could not load input file"); e.printStackTrace(); return; } // Start the sample with the data that was read from the file final byte volumeData[] = data; SwingUtilities.invokeLater(new Runnable() { public void run() { new OpenCLVolumeRenderer( volumeData, sizeX, sizeY, sizeZ, stereoMode); } }); } /** * Stops the animator and calls System.exit() in a new Thread. * (System.exit() may not be called synchronously inside one * of the JOGL callbacks) */ protected void runExit() { if (pbo != 0) { unload(); //gl.glDeleteBuffers(1, new int[]{ pbo }, 0); pbo = 0; } new Thread(new Runnable() { public void run() { animatorL.stop(); if (animatorR != null) { animatorR.stop(); } //System.exit(0); } }).start(); } private Timer playTimer = null; private long frameDuration = 37; @Override protected JPanel createControlPanel(){ JPanel controlPanel = super.createControlPanel(); if (multiFrameMode) { JPanel panel = null; JSlider slider = null; // Time panel = new JPanel(new GridLayout(1, 2)); panel.add(new JLabel("Time:")); slider = new JSlider(0, nFrames-1, 0); slider.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { JSlider source = (JSlider) e.getSource(); time = source.getValue(); } }); slider.setPreferredSize(new Dimension(0, 0)); panel.add(slider); controlPanel.add(panel); JPanel buttons = new JPanel(new GridLayout(1,3)); JButton animateButton = new JButton("play"); animateButton.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent arg0) { if (playTimer == null){ playTimer = new Timer(); playTimer.scheduleAtFixedRate(new TimerTask(){ @Override public void run() { time = (time +1) % nFrames; }}, 0, frameDuration); } else { playTimer.cancel(); playTimer = null; } } } ); buttons.add(animateButton); JButton button = new JButton("-"); button.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent arg0) { frameDuration +=10; if (frameDuration > 1000) frameDuration = 1000; resetTimer(); } } ); buttons.add(button); button = new JButton("+"); button.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent arg0) { frameDuration -=10; if (frameDuration < 10) frameDuration = 10; resetTimer(); } } ); buttons.add(button); controlPanel.add(buttons); } return controlPanel; } public void resetTimer(){ if (playTimer!=null){ playTimer.cancel(); playTimer = new Timer(); playTimer.scheduleAtFixedRate(new TimerTask(){ @Override public void run() { time = (time +1) % nFrames; }}, 0, frameDuration); } } @Override protected void initVolumeBuffer(){ CLImageFormat format = new CLImageFormat(ChannelOrder.RGBA, ChannelType.UNORM_INT8); for (int i = 0; i < nFrames; i++) { hvolumeBuffer = glCtx.createIntBuffer(volumes[i].length, Mem.READ_ONLY); for (int j = 0; j < h_volume.length; j++) { hvolumeBuffer.getBuffer().put(j, (int)volumes[i][j]); } hvolumeBuffer.getBuffer().rewind(); clImages.add(glCtx.createImage3d(hvolumeBuffer.getBuffer(), volumeSize[0], volumeSize[1], volumeSize[2], format, Mem.READ_ONLY, Mem.ALLOCATE_BUFFER)); hvolumeBuffer.release(); try { commandQueue.putWriteImage(clImages.get(i), true) .finish(); } catch (Exception e) { e.printStackTrace(); unload(); } } hvolumeBuffer = null; } @Override protected void setKernelParameters(){ // Set up the execution parameters for the kernel: // - One pointer for the output that is mapped to the PBO // - Two ints for the width and height of the image to render // - Four floats for the visualization parameters of the renderer kernelFunction.rewind() .putArg(d_output) .putArg(width) .putArg(height) .putArg(density) .putArg(brightness) .putArg(transferOffset) .putArg(transferScale) .putArg(invViewMatrix) .putArg(clImages.get(time)) .putArg(transferTex) .rewind(); } /** * release all CL related objects and free memory */ @Override protected void unload(){ if (commandQueue != null) commandQueue.release(); //release all buffers if (clImages != null){ Iterator<CLImage3d<IntBuffer>> it = clImages.iterator(); while (it.hasNext()) { it.next().release(); } } if (tex != null) tex.release(); if (transferTex != null) transferTex.release(); if (invViewMatrix != null) invViewMatrix.release(); if (d_output != null) d_output.release(); if (hvolumeBuffer != null) hvolumeBuffer.release(); if (kernelFunction != null) kernelFunction.release(); if (program != null) program.release(); if (glCtx != null) glCtx.release(); } } /* * Copyright (C) 2010-2014 Martin Berger * CONRAD is developed as an Open Source project under the GNU General Public License (GPL). */