CS 1: Lecture 34 – Volume Slicer
Dear students,
When we last met, we made a slideshow application. This was our first graphical application that we wrote from scratch. We organized the code into two camps: model code and view code. This separation made the model code reusable under many different circumstances. Let’s do all that again today, but in a different context and in more detail. We will create a volume slicer, which will depict a single 2D slice of a 3D dataset.
You probably haven’t gotten your hands on a lot of 3D data. But you might have gotten your hands in one. If you’ve ever had an MRI or CT scan, those machines essentially produced a stack of 2D images at increasing depths—a 3D array. And if you recorded many of these over time, you’d get a 4D array.
Visualizing 3D data is possible, but if you’ve ever had an ultrasound, you know that a lot of measurement and analysis is done in 2D. We’ll make a rudimentary tool that loads in the full 3D volume of data, but only projects a single plane of it. We’ll add a slider so we can change which plane is projected.
Let’s follow this checklist as we implement our slicer:
- Create a
Volume
class that encapsulates a 3D array holding data from a CT scan or MRI. - Read in the density data from a binary file.
- Create a
Slicer
class that is a window. - Add a slider to
Slicer
to change which plane is displayed. - Discuss
interface
, local classes, and lambdas. - Load the currently selected slice of the volume into a
JLabel
as a grayscale image. - Add a
TransferFunction
class for abstracting a colormap. - Colorize the selected slice using the transfer function.
Here’s your TODO to complete before we meet again:
- CS 145, lab 11 is posted. Feel free to start early.
- The current Google Doodle is a coding exercise. I’ve solved all 6 levels, and gotten the shortest code medal on 5/6. Any challengers?
See you next class!
P.S. It’s time for a haiku!
Numbers to pictures
Pictures to interactions
To better humans
P.P.S. Here’s the code we wrote together…
Volume.java
package lecture1204;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class Volume {
private int[][][] densities;
public Volume(String path) throws IOException {
DataInputStream in = new DataInputStream(new FileInputStream(path));
int width = in.readInt();
int height = in.readInt();
int depth = in.readInt();
densities = new int[depth][height][width];
for (int z = 0; z < depth; ++z) {
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
densities[z][y][x] = Byte.toUnsignedInt(in.readByte());
}
}
}
in.close();
}
public int getDepth() {
return densities.length;
}
public int getHeight() {
return densities[0].length;
}
public int getWidth() {
return densities[0][0].length;
}
public int get(int x, int y, int z) {
return densities[z][y][x];
}
}
SliderListener.java
package lecture1204;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class SliderListener implements ChangeListener {
public void stateChanged(ChangeEvent e) {
System.out.println(e);
}
}
Slicer.java
package lecture1204;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.image.BufferedImage;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JSlider;
public class Slicer extends JFrame {
private JLabel label;
private Volume volume;
public Slicer(Volume volume) {
this.volume = volume;
JSlider slider = new JSlider(0, 100);
label = new JLabel("foobag");
add(slider, BorderLayout.NORTH);
add(label, BorderLayout.CENTER);
slider.addChangeListener(e -> {
load(slider.getValue());
});
load(slider.getValue());
pack();
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
private void load(int z) {
BufferedImage image = new BufferedImage(volume.getWidth(), volume.getHeight(), BufferedImage.TYPE_INT_RGB);
for (int y = 0; y < image.getHeight(); ++y) {
for (int x = 0; x < image.getWidth(); ++x) {
int density = volume.get(x, y, z);
Color color = new Color(density, density, density);
image.setRGB(x, y, color.getRGB());
}
}
label.setIcon(new ImageIcon(image));
}
}
Main.java
package lecture1204;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
Volume vismale = new Volume("/Users/johnch/Desktop/vismale.f0");
Slicer slicer = new Slicer(vismale);
}
}