CS 145 Lecture 39 – Volume Slicer
Agenda
- what ?s
- sequencer
- volume slicer
TODO
- Prepare a question for the last quiz during Monday’s lab. Worth a 1/4 sheet. Credit will only be awarded to those who share, so you may want to have several prepared, in case one gets asked by someone else.
- Last quarter sheet of the semester: compose a headline (as in a newspaper or Buzzfeed) to summarize your learning this semester. Turn it in on a 1/4 sheet at the beginning of Monday’s lecture.
- Don’t forget to email me when you’ve finished homework 7 for Mercy Fortnight. This is a required step.
Code
Gif
Vector2D.java
package lecture1205;
public class Vector2D {
public double x;
private double y;
private double length;
public Vector2D() {
System.out.println("ctor");
}
public Vector2D(double x, double y) {
this.x = x;
this.y = y;
length = Math.hypot(x, y);
}
public double getLength() {
return length;
}
public void setX(double newX) {
x = newX;
length = Math.hypot(x, y);
}
public static Vector2D Vector2D() {
System.out.println("static");
return null;
}
public static void main(String[] args) {
Vector2D v = new Vector2D();
Vector2D vv = new Vector2D(10, 20);
System.out.println(vv.getLength());
vv.x = Double.MAX_VALUE;
System.out.println(vv.getLength());
}
}
Volume.java
package lecture1205;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
public class Volume {
private int[][][] voxels;
public Volume(String path) throws IOException {
LEDataInputStream in = new LEDataInputStream(path);
int width = in.readInt();
int height = in.readInt();
int depth = in.readInt();
System.out.printf("%d %d %d", width, height, depth);
voxels = 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) {
voxels[z][y][x] = in.read();
}
}
}
in.close();
}
public int getWidth() {
return voxels[0][0].length;
}
public int getHeight() {
return voxels[0].length;
}
public int getDepth() {
return voxels.length;
}
public int getVoxel(int x, int y, int z) {
return voxels[z][y][x];
}
}
TransferFunction.java
package lecture1205;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class TransferFunction {
private BufferedImage colors;
public TransferFunction(String path) throws IOException {
colors = ImageIO.read(new File(path));
}
public int lookup(float proportion) {
int i = (int) (proportion * (colors.getWidth() - 1));
return colors.getRGB(i, 0);
}
}
Renderer.java
package lecture1205;
import java.awt.image.BufferedImage;
public class Renderer {
public static void transverse(Volume volume,
TransferFunction function,
String outPath) {
GifSequenceWriter giffer = new GifSequenceWriter(outPath, 100, true);
for (int y = 0; y < volume.getHeight(); ++y) {
BufferedImage frame = new BufferedImage(volume.getWidth(), volume.getDepth(), BufferedImage.TYPE_INT_RGB);
for (int z = 0; z < volume.getDepth(); ++z) {
for (int x = 0; x < volume.getWidth(); ++x) {
int intensity = volume.getVoxel(x, y, z);
int color = function.lookup(intensity / 255.0f);
frame.setRGB(x, z, color);
}
}
giffer.appendFrame(frame);
}
giffer.close();
}
public static void coronal(Volume volume,
TransferFunction function,
String outPath) {
GifSequenceWriter giffer = new GifSequenceWriter(outPath, 100, true);
for (int z = 0; z < volume.getDepth(); ++z) {
BufferedImage frame = new BufferedImage(volume.getWidth(), volume.getHeight(), BufferedImage.TYPE_INT_RGB);
for (int y = 0; y < volume.getHeight(); ++y) {
for (int x = 0; x < volume.getWidth(); ++x) {
int density = volume.getVoxel(x, y, z);
int color = function.lookup(density / 255.0f);
frame.setRGB(x, y, color);
}
}
giffer.appendFrame(frame);
}
giffer.close();
}
}
LEDataInputStream.java
package lecture1205;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
/**
* A class for reading primitives from a file in little endian format.
*/
public class LEDataInputStream {
/** The underlying big endian file reader. */
private DataInputStream in;
/** A collection of bytes used for an endian switcharoo. */
private byte[] bytes;
/** An endian switcher. */
private ByteBuffer buffer;
/**
* Create a new little endian file reader for the specified file.
*
* @param file
* Name of file to reader.
*
* @throws FileNotFoundException
*/
public LEDataInputStream(String file) throws FileNotFoundException {
in = new DataInputStream(new FileInputStream(file));
bytes = new byte[4];
buffer = ByteBuffer.wrap(bytes);
buffer.order(ByteOrder.LITTLE_ENDIAN);
}
/**
* Read the next int from the file.
*
* @return
* Value read.
*
* @throws IOException
*/
public int readInt() throws IOException {
in.read(bytes, 0, 4);
return buffer.getInt(0);
}
/**
* Read the next short from the file.
*
* @return
* Value read.
*
* @throws IOException
*/
public short readShort() throws IOException {
in.read(bytes, 0, 2);
return buffer.getShort(0);
}
/**
* Read a sequence of bytes from the file. The number of bytes read is determined by the length of the array.
*
* @param bytes
* Array in which read bytes are stored.
*
* @throws IOException
*/
public void read(byte[] bytes) throws IOException {
in.read(bytes);
}
public int read() throws IOException {
return in.read();
}
/**
* Read a sequence of shorts from the file. The number of shorts read is determined by the length of the array.
*
* @param shorts
* Array in which shorts are stored.
*
* @throws IOException
*/
public void read(short[] shorts) throws IOException {
for (int i = 0; i < shorts.length; ++i) {
shorts[i] = readShort();
}
}
/**
* Close the file.
*
* @throws IOException
*/
public void close() throws IOException {
in.close();
}
}
GifSequenceWriter.java
package lecture1205;
//
// GifSequenceWriter.java
//
// Created by Elliot Kroo on 2009-04-25.
//
// This work is licensed under the Creative Commons Attribution 3.0 Unported
// License. To view a copy of this license, visit
// http://creativecommons.org/licenses/by/3.0/ or send a letter to Creative
// Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA.
import javax.imageio.*;
import javax.imageio.metadata.*;
import javax.imageio.stream.*;
import java.awt.image.*;
import java.io.*;
import java.util.Iterator;
public class GifSequenceWriter {
protected ImageWriter gifWriter;
protected ImageWriteParam imageWriteParam;
protected IIOMetadata imageMetaData;
protected ImageOutputStream outputStream;
/**
* Creates a new GifSequenceWriter
*
* @param outputStream
* the ImageOutputStream to be written to
* @param imageType
* one of the imageTypes specified in BufferedImage
* @param timeBetweenFramesMS
* the time between frames in miliseconds
* @param loopContinuously
* wether the gif should loop repeatedly
* @throws IIOException
* if no gif ImageWriters are found
*
* @author Elliot Kroo (elliot[at]kroo[dot]net)
*/
public GifSequenceWriter(String outputPath,
int timeBetweenFramesMS,
boolean loopContinuously) {
try {
// my method to create a writer
gifWriter = getWriter();
imageWriteParam = gifWriter.getDefaultWriteParam();
ImageTypeSpecifier imageTypeSpecifier = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB);
imageMetaData = gifWriter.getDefaultImageMetadata(imageTypeSpecifier, imageWriteParam);
String metaFormatName = imageMetaData.getNativeMetadataFormatName();
IIOMetadataNode root = (IIOMetadataNode) imageMetaData.getAsTree(metaFormatName);
IIOMetadataNode graphicsControlExtensionNode = getNode(root, "GraphicControlExtension");
graphicsControlExtensionNode.setAttribute("disposalMethod", "none");
graphicsControlExtensionNode.setAttribute("userInputFlag", "FALSE");
graphicsControlExtensionNode.setAttribute("transparentColorFlag", "FALSE");
graphicsControlExtensionNode.setAttribute("delayTime", Integer.toString(timeBetweenFramesMS / 10));
graphicsControlExtensionNode.setAttribute("transparentColorIndex", "0");
IIOMetadataNode commentsNode = getNode(root, "CommentExtensions");
commentsNode.setAttribute("CommentExtension", "Created by MAH");
IIOMetadataNode appEntensionsNode = getNode(root, "ApplicationExtensions");
IIOMetadataNode child = new IIOMetadataNode("ApplicationExtension");
child.setAttribute("applicationID", "NETSCAPE");
child.setAttribute("authenticationCode", "2.0");
int loop = loopContinuously ? 0 : 1;
child.setUserObject(new byte[]{
0x1,
(byte) (loop & 0xFF),
(byte) ((loop >> 8) & 0xFF)
});
appEntensionsNode.appendChild(child);
imageMetaData.setFromTree(metaFormatName, root);
outputStream = new FileImageOutputStream(new File(outputPath));
gifWriter.setOutput(outputStream);
gifWriter.prepareWriteSequence(null);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public void appendFrame(BufferedImage img) {
try {
gifWriter.writeToSequence(new IIOImage(img, null, imageMetaData), imageWriteParam);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* Close this GifSequenceWriter object. This does not close the underlying
* stream, just finishes off the GIF.
*/
public void close() {
try {
gifWriter.endWriteSequence();
outputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* Returns the first available GIF ImageWriter using
* ImageIO.getImageWritersBySuffix("gif").
*
* @return a GIF ImageWriter object
* @throws IIOException
* if no GIF image writers are returned
*/
private static ImageWriter getWriter() throws IIOException {
try {
Iterator<ImageWriter> iter = ImageIO.getImageWritersBySuffix("gif");
if (!iter.hasNext()) {
throw new IIOException("No GIF Image Writers Exist");
} else {
return iter.next();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* Returns an existing child node, or creates and returns a new child node (if
* the requested node does not exist).
*
* @param rootNode
* the <tt>IIOMetadataNode</tt> to search for the child node.
* @param nodeName
* the name of the child node.
*
* @return the child node, if found or a new node created with the given name.
*/
private static IIOMetadataNode getNode(IIOMetadataNode rootNode,
String nodeName) {
int nNodes = rootNode.getLength();
for (int i = 0; i < nNodes; i++) {
if (rootNode.item(i).getNodeName().compareToIgnoreCase(nodeName) == 0) {
return ((IIOMetadataNode) rootNode.item(i));
}
}
IIOMetadataNode node = new IIOMetadataNode(nodeName);
rootNode.appendChild(node);
return node;
}
}
Haiku
on assigning responsibility:
Do I milk the cow?
Or do I decow the milk?
I’m split half and half
Do I milk the cow?
Or do I decow the milk?
I’m split half and half