/* * Copyright (c) 2005 * Brad Kimmel. * Aravind Krishnaswamy. * Michael Lam. * All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * -Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * -Redistribution in binary form must reproduct the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the distribution. * * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT * BE LIABLE FOR ANY DAMAGES OR LIABILITIES SUFFERED BY LICENSEE AS A RESULT * OF OR RELATING TO USE, MODIFICATION OR DISTRIBUTION OF THE SOFTWARE OR ITS * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE SOFTWARE, EVEN * IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. * * You acknowledge that Software is not designed, licensed or intended for * use in the design, construction, operation or maintenance of any nuclear * facility. */ /* * @(#)MetropolisSamplingApplet.java 1.00 03/28/2005 */ import java.awt.*; import java.awt.event.*; import java.applet.*; import java.awt.image.BufferedImage; /** *

Title: MetropolisSamplingApplet

* *

Description: The applet

* *

Copyright: Copyright (c) 2005

* *

Company:

* @author Aravind Krishnaswamy * @version 1.0 */ public class MetropolisSamplingApplet extends Applet { MetropolisAppletImagePanels images; MetropolisSamplingControls controls; /** * Initializes the controls */ public void init() { setLayout(new BorderLayout()); images = new MetropolisAppletImagePanels(); controls = new MetropolisSamplingControls(images, this); add("Center", images); add("South", controls); } /** * Takes everything down */ public void destroy() { remove(images); remove(controls); } public static void main(String args[]) { Frame f = new Frame("Metropolis Sampling test"); MetropolisSamplingApplet metroApplet = new MetropolisSamplingApplet(); metroApplet.init(); metroApplet.start(); f.add("Center", metroApplet); f.setSize(512, 500); f.show(); } public String getAppletInfo() { return "Metropolis Sampling test by Brad Kimmel, Aravind Krishnaswamy, Michael Lam"; } } /** *

Title: ISampler

* *

Description: Abstract interface to a sampler

* *

Copyright: Copyright (c) 2005

* * @author Aravind Krishnaswamy * @version 1.0 */ abstract interface ISampler { /** * Generates the motion blurred image */ public void GenerateMotionBlur(); /** * Retreives the value at the given pixel * * @param x int * @param y int * @return double */ public double GetPixelValue(int x, int y); } /** *

Title: SamplingDrawPanel

* *

Description: The draw panel that contains a motion blurred image

* *

Copyright: Copyright (c) 2005

* *

Company:

* @author Aravind Krishnaswamy * @version 1.0 */ class SamplingDrawPanel extends Panel { public ISampler sampler; private BufferedImage output; private double lastX = 0; private double lastY = 0; /** * Constructor * * @param width int * @param height int * @param sampler ISampler */ public SamplingDrawPanel(int width, int height, ISampler sampler) { output = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); this.sampler = sampler; GenerateMotionBlurImage(); } /** * Redraws the image * * @param g Graphics */ public void paint(Graphics g) { g.clearRect(0, 0, this.getSize().width, this.getSize().height); g.drawImage(output, 0, 0, this); } /** * Generates the motion blurred image by using the sampler */ public void GenerateMotionBlurImage() { // Tell the sampler to generate the image sampler.GenerateMotionBlur(); for (int y = 0; y < output.getHeight(); y++) { for (int x = 0; x < output.getWidth(); x++) { double pixel_value = sampler.GetPixelValue(x, y); int rgb = 0xFF000000 | ((int) (pixel_value * 255.0) << 16) | ((int) (pixel_value * 255.0) << 8) | (int) (pixel_value * 255.0); output.setRGB(x, y, rgb); } } } } /** *

Title: MetropolisAppletImagePanels

* *

Description: The image panels container

* *

Copyright: Copyright (c) 2005

* *

Company:

* @author not attributable * @version 1.0 */ class MetropolisAppletImagePanels extends Panel { public SamplingDrawPanel metropolisPanel; public SamplingDrawPanel randomPanel; public MetropolisAppletImagePanels() { setLayout(new GridLayout(1, 2)); metropolisPanel = new SamplingDrawPanel(256, 256, new MetropolisSampling(256, 256)); randomPanel = new SamplingDrawPanel(256, 256, new RandomSampling(256, 256)); add("West", metropolisPanel); add("East", randomPanel); } public void destroy() { remove(metropolisPanel); remove(randomPanel); } } /** *

Title: MetropolisSamplingControls

* *

Description: UI controls for the metropolis sampling test

* *

Copyright: Copyright (c) 2005

* *

Company:

* @author Aravind Krishnaswamy * @version 1.0 */ class MetropolisSamplingControls extends Panel implements ActionListener { private MetropolisAppletImagePanels target; private MetropolisSamplingApplet applet; private TextField pixelJitterX; private TextField pixelJitterY; private TextField timeJitter; private TextField numSamples; private Button button; public MetropolisSamplingControls(MetropolisAppletImagePanels img, MetropolisSamplingApplet applet) { this.target = img; this.applet = applet; GridLayout gridLayout = new GridLayout(6, 2); gridLayout.setHgap(10); gridLayout.setVgap(10); setLayout(gridLayout); // Add the buttons and stuff here add(new Label("Pixel Jitter X")); add(pixelJitterX = new TextField(Double.toString(((MetropolisSampling) ( target.metropolisPanel. sampler)).pixelJitterX), 4)); add(new Label("Pixel Jitter Y")); add(pixelJitterY = new TextField(Double.toString(((MetropolisSampling) ( target.metropolisPanel. sampler)).pixelJitterY), 4)); add(new Label("Time Jitter")); add(timeJitter = new TextField(Double.toString(((MetropolisSampling) ( target.metropolisPanel. sampler)).timeJitter), 4)); add(new Label("Num Samples")); add(numSamples = new TextField(Integer.toString(((MetropolisSampling) ( target.metropolisPanel. sampler)).numSamples), 8)); add(button = new Button("Generate Image")); button.addActionListener(this); } /** * Retreives the values from the text boxes */ public void getParams() { try { ((MetropolisSampling) (target.metropolisPanel.sampler)). pixelJitterX = Double.parseDouble( pixelJitterX.getText()); } catch (NumberFormatException e) { ((MetropolisSampling) (target.metropolisPanel.sampler)). pixelJitterX = 0.03; } try { ((MetropolisSampling) (target.metropolisPanel.sampler)). pixelJitterY = Double.parseDouble( pixelJitterY.getText()); } catch (NumberFormatException e) { ((MetropolisSampling) (target.metropolisPanel.sampler)). pixelJitterY = 0.03; } try { ((MetropolisSampling) (target.metropolisPanel.sampler)).timeJitter = Double.parseDouble( timeJitter.getText()); } catch (NumberFormatException e) { ((MetropolisSampling) (target.metropolisPanel.sampler)).timeJitter = 0.05; } try { ((MetropolisSampling) (target.metropolisPanel.sampler)).numSamples = Integer.parseInt( numSamples.getText()); ((RandomSampling) (target.randomPanel.sampler)).numSamples = Integer. parseInt( numSamples.getText()); } catch (NumberFormatException e) { ((MetropolisSampling) (target.metropolisPanel.sampler)).numSamples = 1000000; ((RandomSampling) (target.randomPanel.sampler)).numSamples = 1000000; } } /** * Called when the user clicks the button **/ public void actionPerformed(ActionEvent e) { if (e.getSource() == button) { // We need to let the user know we are doing work applet.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); target.setVisible(false); getParams(); target.metropolisPanel.GenerateMotionBlurImage(); target.randomPanel.GenerateMotionBlurImage(); target.metropolisPanel.repaint(); target.randomPanel.repaint(); applet.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); target.setVisible(true); } } } /** *

Title: FuncParams

* *

Description: Structure for internally holding function parameters and * the resultant values

* *

Copyright: Copyright (c) 2005

* *

Company:

* @author Aravind Krishnaswamy * @version 1.0 */ class FuncParams { public double x, y, time, val; FuncParams() { } FuncParams(FuncParams other) { x = other.x; y = other.y; time = other.time; val = other.val; } FuncParams(double x_, double y_, double time_) { x = x_; y = y_; time = time_; val = -1; } } /** *

Title: Metropolis Sampling

* *

Description: Implementation of a 3 dimensional metropolis sampler

* *

Copyright: Copyright (c) 2005

* *

Company:

* @author Brad Kimmel, Aravind Krishnaswamy, Michael Lam * @version 1.0 */ class MetropolisSampling implements ISampler { public double pixelJitterX = 0.04; public double pixelJitterY = 0.04; public double timeJitter = 0.05; public int numSamples = 100000; double imagebuf[][]; int imageWidth; int imageHeight; /** * The motion blur function itself */ MotionBlurFunction func = new MotionBlurFunction(); /** * Main constructor * * @param width int * @param height int */ public MetropolisSampling(int width, int height) { imageWidth = width; imageHeight = height; imagebuf = new double[imageWidth][imageHeight]; } /** * Detemines whether a particular transition from one sample to the other is * to be accepted. * * @param p1 Sample set to transition from * @param p2 Sample set to transition to * @return true is the transition to be accepted, false if we are to stay * with the current sample */ private boolean acceptTransition(FuncParams p1, FuncParams p2) { if (p1.val > 0) { return (Math.random() < (p2.val / p1.val)); } else { return true; } } /** * Computes a transition from the given sample to a new sample * * @param p The current sample * @return New sample we transition to */ public FuncParams transition(FuncParams p) { if (Math.random() < 0.1) { return new FuncParams(Math.random(), Math.random(), Math.random()); } FuncParams ret = new FuncParams(); ret.x = p.x + 2.0 * (Math.random() - 0.5) * pixelJitterX; ret.y = p.y + 2.0 * (Math.random() - 0.5) * pixelJitterY; ret.time = p.time + 2.0 * (Math.random() - 0.5) * timeJitter; return ret; } /** * Generates a new sample given the previous sample * * @param p The previous sample * @return The new sample */ public FuncParams GenerateSample(FuncParams p) { FuncParams temp = transition(p); temp.val = func.Evaluate(temp.x, temp.y, temp.time); if (acceptTransition(p, temp)) { return temp; } return p; } /** * Computes the image using Metropolis sampling */ public void GenerateMotionBlur() { // Clear out for (int y = 0; y < imageHeight; y++) { for (int x = 0; x < imageWidth; x++) { imagebuf[x][y] = 0; } } // Take n samples FuncParams p = new FuncParams(Math.random(), Math.random(), Math.random()); p.val = func.Evaluate(p.x, p.y, p.time); for (int i = 0; i < numSamples; i++) { FuncParams ret = GenerateSample(p); int dx = (int) Math.floor(ret.x * (double) imageWidth); int dy = (int) Math.floor(ret.y * (double) imageHeight); if (dx < imageWidth && dx >= 0 && dy < imageHeight && dy >= 0) { imagebuf[dx][dy] += ret.val; } p = ret; } double max = 0; for (int y = 0; y < imageHeight; y++) { for (int x = 0; x < imageWidth; x++) { if (imagebuf[x][y] > max) { max = imagebuf[x][y]; } } } // Average out for (int y = 0; y < imageHeight; y++) { for (int x = 0; x < imageWidth; x++) { imagebuf[x][y] /= max; } } } /** * Retreives pixel value * * @param x int * @param y int * @return double */ public double GetPixelValue(int x, int y) { if (x < imageWidth && x >= 0 && y < imageHeight && y >= 0) { return imagebuf[x][y]; } return 0; } } /** *

Title: RandomSampling

* *

Description: Implements a pure random sampling algorithm

* *

Copyright: Copyright (c) 2005

* *

Company:

* @author Aravind Krishnaswamy * @version 1.0 */ class RandomSampling implements ISampler { public int numSamples = 100000; double imagebuf[][]; int counts[][]; int imageWidth; int imageHeight; /** * The motion blur function */ MotionBlurFunction func = new MotionBlurFunction(); /** * Constructor * * @param width int * @param height int */ public RandomSampling(int width, int height) { imageWidth = width; imageHeight = height; imagebuf = new double[imageWidth][imageHeight]; counts = new int[imageWidth][imageHeight]; } /** * Retreives the value for the requested pixel * * @param x int * @param y int * @return double */ public double GetPixelValue(int x, int y) { if (x < imageWidth && x >= 0 && y < imageHeight && y >= 0) { return imagebuf[x][y]; } return 0; } /** * Computes motion blur using a pure random sampling algorithm */ public void GenerateMotionBlur() { // Clear out for (int y = 0; y < imageHeight; y++) { for (int x = 0; x < imageWidth; x++) { imagebuf[x][y] = 0; counts[x][y] = 0; } } // Take n samples for (int i = 0; i < numSamples; i++) { double x = Math.random(); double y = Math.random(); int dx = (int) Math.floor(x * (double) imageWidth); int dy = (int) Math.floor(y * (double) imageHeight); double time = Math.random(); imagebuf[dx][dy] += func.Evaluate(x, y, time); counts[dx][dy]++; } // Average out for (int y = 0; y < imageHeight; y++) { for (int x = 0; x < imageWidth; x++) { if (counts[x][y] > 0) { imagebuf[x][y] = imagebuf[x][y] / (double) counts[x][y]; } else { imagebuf[x][y] = 0; } } } } } /** *

Title: MotionBlurFunction

* *

Description: A simple three dimensional function for determining whether a * sphere is visible at a pixel location at a specific time

* *

Copyright: Copyright (c) 2005

* *

Company:

* @author Aravind Krishnaswamy * @version 1.0 */ class MotionBlurFunction { private final double sphereRadius = 0.08; /** * Computes the distance between two points * * @param x1 x coordinate of first point * @param y1 y coordinate of first point * @param x2 x coordinate of second point * @param y2 y coordinate of second point * @return distance between the two points */ private double distanceBetweenTwoPoints(double x1, double y1, double x2, double y2) { double dx2 = (x1 - x2) * (x1 - x2); double dy2 = (y1 - y2) * (y1 - y2); return Math.sqrt(dx2 + dy2); } /** * Evaluates the function for the given x, y and time values * * @param x x pixel co-ordinate (NDC) * @param y y pixel coordinate (NDC) * @param time time (NTC) * @return returns 1 if there is a sphere at that pixel location at that * time, 0 otherwise */ public double Evaluate(double x, double y, double time) { if (time > 1.0) { return 0.0; } if (x < 0 || x > 1.0) { return 0.0; } if (y < 0 || y > 1.0) { return 0.0; } double x0 = 0.1; for (int i = 0; i < 5; i++) { double ynew = 0.1 + 0.2 * (double) i; double x1 = ynew; double xnew = x0 + time * (x1 - x0); if (distanceBetweenTwoPoints(xnew, ynew, x, y) < sphereRadius) { return 1.0; } } return 0.0; } }