package edu.stanford.rsl.conrad.utils;
import java.io.Serializable;
import ij.ImageJ;
import ij.process.FloatProcessor;
/**
* Class for interpolation on an arbitrary regular 2-D grid.
* (Inplementation follows formula as displayed in Wikipedia.)
*
* @see <a href="http://en.wikipedia.org/wiki/Bilinear_interpolation">http://en.wikipedia.org/wiki/Bilinear_interpolation</a>
*
* @author akmaier
*
*/
public class BilinearInterpolatingDoubleArray implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1183806565056190746L;
private double [] meshx;
private double [] meshy;
private double [][] values;
private double minx;
private double maxx;
private double miny;
private double maxy;
/**
* Constructor with requires an x-mesh, a y-mesh, and a two-dimensional array of values at the mesh points
* @param meshx the mesh spacing in x direction
* @param meshy the mesh spacing in y direction
* @param values the values at the mesh points.
*/
public BilinearInterpolatingDoubleArray(double [] meshx, double [] meshy, double [][] values){
this.meshx = meshx;
this.meshy = meshy;
this.values = values;
double [] minmax = DoubleArrayUtil.minAndMaxOfArray(meshx);
minx = minmax[0];
maxx = minmax[1];
minmax = DoubleArrayUtil.minAndMaxOfArray(meshy);
miny = minmax[0];
maxy = minmax[1];
}
/**
* Interpolate an arbitrary point between the meshes.
*
* @param meshPointX the x-coordinate
* @param meshPointY the y-coordinate
* @return the interpolated value
* @throws Exception may happen if the point is not within the grid.
*/
public double getValue(double meshPointX, double meshPointY) throws Exception{
if ((meshPointX < minx)||(meshPointX > maxx)){
throw new Exception("Cannot extrapolate outside mesh x-axis: " + meshPointX + " Range: [ " + minx + ", " + maxx + " ]");
}
if ((meshPointX < miny)||(meshPointY > maxy)){
throw new Exception("Cannot extrapolate outside mesh y-axis: " + meshPointY + " Range: [ " + miny + ", " + maxy + " ]");
}
int lowerIndexx = 0;
for (int i = 1; i < meshx.length; i++){
if (meshx[i] > meshPointX){
lowerIndexx = i - 1;
break;
}
}
int lowerIndexy = 0;
for (int i = 1; i < meshy.length; i++){
if (meshy[i] > meshPointY){
lowerIndexy = i - 1;
break;
}
}
// denominator in interpolation formula (area of the interpolation patch)
double denominator = (meshx[lowerIndexx+1] - meshx[lowerIndexx])*(meshy[lowerIndexy+1] - meshy[lowerIndexy]);
// Interpolation value at values[lowerIndexx][lowerIndexy]
double revan = values[lowerIndexx][lowerIndexy] * (meshx[lowerIndexx+1] - meshPointX) * (meshy[lowerIndexy+1] - meshPointY);
// Interpolation value at values[lowerIndexx+1][lowerIndexy]
revan += values[lowerIndexx+1][lowerIndexy] * (meshPointX - meshx[lowerIndexx]) * (meshy[lowerIndexy+1] - meshPointY);
// Interpolation value at values[lowerIndexx][lowerIndexy+1]
revan += values[lowerIndexx][lowerIndexy+1] * (meshx[lowerIndexx+1] - meshPointX) * (meshPointY - meshy[lowerIndexy]);
// Interpolation value at values[lowerIndexx+1][lowerIndexy+1]
revan += values[lowerIndexx+1][lowerIndexy+1] * (meshPointX - meshx[lowerIndexx]) * (meshPointY - meshy[lowerIndexy]);
// devide by area;
revan /= denominator;
return revan;
}
/**
* Code for testing using CONRAD Software package. Will create the example image as displayed in Wikipedia.<BR>
* (http://en.wikipedia.org/wiki/File:Bilininterp.png)
*
* @param args
*/
public static void main (String[] args){
double [] array1 = {0 ,1};
double [] array2 = {0, 1};
double [] [] arrayVal = { {1, 0}, {0.5, 1}};
BilinearInterpolatingDoubleArray bil = new BilinearInterpolatingDoubleArray(array1, array2, arrayVal);
FloatProcessor fl = bil.toFloatProcessor(640, 480);
VisualizationUtil.showImageProcessor(fl,"Interpolation Example as in Wikipedia");
}
/**
* Renders a float processor of the 2-D array given the number of bins in x and y direction
* @param binsX the number of bins in x direction
* @param binsY the number of bins in y direction
* @return the rendered FloatProcessor.
*/
public FloatProcessor toFloatProcessor(int binsX, int binsY){
float [] pixels = new float [binsX * binsY];
double rangeX = maxx - minx;
double rangeY = maxy - miny;
for (int i= 0; i < binsX; i++){
for (int j = 0; j < binsY; j++){
try {
double coordX = minx + ((((double)i)/binsX) * rangeX);
double coordY = miny + ((((double)j)/binsY) * rangeY);
pixels[(j*binsX)+i] = (float) this.getValue(coordX, coordY);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
new ImageJ();
return new FloatProcessor(binsX, binsY, pixels, ImageUtil.getDefaultColorModel());
}
}
/*
* Copyright (C) 2010-2014 Andreas Maier
* CONRAD is developed as an Open Source project under the GNU General Public License (GPL).
*/