teaching machines

CS 1: Lecture 34 – Volume Slicer

December 4, 2017 by . Filed under cs1, cs145, cs148, fall 2017, lectures.

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:

Here’s your TODO to complete before we meet again:

See you next class!

Sincerely,

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);
  }
}