package fable.imageviewer.component;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.embl.cca.utils.imageviewer.PointWithValueIIF;
import fable.imageviewer.component.ImageComponent;
/**
* The <code>PSF</code> class contains parameters for applying
* a PSF effect to an image.
* <p>
*
* @author Gabor Naray
* @version 1.00 07/12/2011
* @since 20111207
*/
public class PSF {
protected int radius;
protected int kernel[][] = null;
protected int kernelCenter;
public PSF( int radius ) {
this.radius = radius;
calculateGaussKernel();
}
// protected void calculateOrbKernel() {
// final double r2 = Math.pow( radius, 2);
// final int kernelLength = radius * 2 + 1 - 2; // Two edges are full 0
// kernelCenter = ( kernelLength - 1 ) / 2;
// kernel = new int[ kernelLength ][ kernelLength ];
// for( int j = 0; j < kernelLength; j++ ) {
// double oneMinusj2 = 1 - Math.pow( j - kernelCenter, 2 ) / r2;
// for( int i = 0; i < kernelLength; i++ ) {
// kernel[ j ][ i ]
// = (int)( Math.sqrt( oneMinusj2 - Math.pow( i - kernelCenter, 2 ) / r2 ) * 100 );
// }
// }
// }
//
// protected void calculateGaussLikeKernel() {
// final int kernelLength = 9;
// kernelCenter = ( kernelLength - 1 ) / 2;
// kernel = new int[][] {
// { 7, 10, 15, 18, 20, 18, 15, 10, 7 },
// { 10, 16, 25, 35, 40, 35, 25, 16, 10 },
// { 15, 25, 45, 70, 80, 70, 45, 25, 15 },
// { 18, 35, 70, 85, 95, 85, 70, 35, 18 },
// { 20, 40, 80, 95, 99, 95, 80, 40, 20 },
// { 18, 35, 70, 85, 95, 85, 70, 35, 18 },
// { 15, 25, 45, 70, 80, 70, 45, 25, 15 },
// { 10, 16, 25, 35, 40, 35, 25, 16, 10 },
// { 7, 10, 15, 18, 20, 18, 15, 10, 7 } };
// }
protected void calculateGaussKernel() {
final double r2 = Math.pow( radius, 2);
final int kernelLength = radius * 2 + 1;
kernelCenter = ( kernelLength - 1 ) / 2;
kernel = new int[ kernelLength ][ kernelLength ];
final double c = 2.2; //standard deviation (sigma)
final double b = 0; //mean (mu)
final double a = 100; //In equation: 1 / ( c * Math.sqrt( 2*Math.PI ) ), but we want constant
final double c2m2 = 2 * Math.pow( c, 2 );
for( int j = 0; j < kernelLength; j++ ) {
final double j2 = Math.pow( j - kernelCenter, 2 );
for( int i = 0; i < kernelLength; i++ ) {
kernel[ j ][ i ] = (int)( a * Math.pow( Math.E,
-Math.pow( Math.sqrt( Math.pow( i - kernelCenter, 2 ) + j2 ) - b, 2 )
/ c2m2 ) );
}
}
}
public float[] applyPSF(ImageComponent imageComponent, Rectangle imageRect) {
// long t0 = System.nanoTime(), t1 = t0;
float[] imageValues = null;
if (!imageComponent.isImageDiffOn()) {
imageValues = imageComponent.getImageModel().getData(imageRect);
} else {
imageValues = imageComponent.getImageDiffModel().getData(imageRect);
}
/* Since Point2DWithValue are ordered by value ascending, applying the PSF
* in this order can be done in the gotten imageValues, because the smaller
* valued point does not disturb the higher valued point. Thus we do not
* have to clone the imageValues array, which saves time.
*/
// float[] newImageValues = new float[ imageValues.length ];
// System.arraycopy( imageValues, 0, newImageValues, 0, imageValues.length );
do {
PointWithValueIIF[] PSFPoints = imageComponent.getStatistics().getPSFPoints();
if( !imageComponent.isPSFOn() || PSFPoints.length == 0 )
break;
final int kernelMaxValue = kernel[ kernelCenter ][ kernelCenter ];
// t1 = System.nanoTime();
// System.out.println( "DEBUG: applyPSF.begin.dt [msec]= " + ( t1 - t0 ) / 1000000 );
// long t11 = System.nanoTime();
// System.out.println( "DEBUG: SYSTEMOUTPRINTLN.dt [msec]= " + ( t11 - t1 ) / 1000000 );
for( int k = 0; k < PSFPoints.length; k++ ) {
int x = PSFPoints[ k ].x;
int y = PSFPoints[ k ].y;
int middle = y * imageRect.width + x;
float valueMiddle = imageValues[ middle ] / kernelMaxValue;
int yMin = Math.max( y - kernelCenter, 0);
int yMax = Math.min( y + kernelCenter, imageRect.height - 1 );
int xMin = Math.max( x - kernelCenter, 0);
int xMax = Math.min( x + kernelCenter, imageRect.width - 1 );
int topLeftOffset = yMin * imageRect.width + xMin;
int jMin = yMin - y + kernelCenter;
int jMax = yMax - y + kernelCenter;
int iMin = xMin - x + kernelCenter;
int iMax = xMax - x + kernelCenter;
int iXY = topLeftOffset;
int dXY = imageRect.width - ( iMax + 1 - iMin );
float valueIJ;
for( int j = jMin; j <= jMax; iXY += dXY, j++ ) {
for( int i = iMin; i <= iMax; iXY++, i++ ) {
//Ignoring 0 kernelvalue and protecting the gaps
if( kernel[j][i] == 0 || imageValues[ iXY ] < 0 )
continue;
valueIJ = kernel[j][i] * valueMiddle;
if( imageValues[ iXY ] < valueIJ )
imageValues[ iXY ] = valueIJ;
}
}
}
} while( false );
// long t2 = System.nanoTime();
// System.out.println( "DEBUG: applyPSF.multiplying.dt [msec]= " + ( t2 - t1 ) / 1000000 );
return imageValues;
}
}