package fr.unistra.pelican;
import fr.unistra.pelican.DoubleImage;
import fr.unistra.pelican.Image;
import fr.unistra.pelican.PelicanException;
/**
* Integral image as described in :
*
* Herbert Bay, Andreas Ess, Tinne Tuytelaars, Luc Van Gool, "SURF: Speeded Up Robust Features",
* Computer Vision and Image Understanding (CVIU), Vol. 110, No. 3, pp. 346--359, 2008
* ( Get it there : http://www.vision.ee.ethz.ch/~surf/papers.html )
*
* "Given an input image In and a point (x,y) the integral image I is calculated by the sum of
* the values between the point and the origin. Formally this can be defined by the formula :
* i<=x j<=y
* I(x,y) = E E IN(x,y)
* i=0 j=0
* Using the integral image, the task of calculating the area of an upright rectangular region
* is reduced four operations. If we consider a rectangle bounded by vertices A, B, C and D,
* the sum of pixel intensities is calculated by A+D-(C+B). Since computation time is invariant
* to change in size this approach is particularly useful when large areas are required."
*
* Integral image for : - ByteImage and IntegerImage is an IntegerImage
* - DoubleImage and BooleanImage is a DoubleImage
*
* Be careful when dealing with high pixel values, there is risk of max value overflow (depending on image type)
*
* @author Régis Witz, Jonathan Weber
*/
public class IntegralImage extends Image {
Image integralImage;
public IntegralImage( Image input ) {
this.xdim = input.getXDim();
this.ydim = input.getYDim();
this.zdim = input.getZDim();
this.tdim = input.getTDim();
this.bdim = input.getBDim();
if(input instanceof ByteImage)
{
this.integralImage = new IntegerImage( xdim,ydim,zdim,tdim,bdim );
int sum;
for ( int t = 0 ; t < this.tdim ; t++ )
for ( int z = 0 ; z < this.tdim ; z++ )
for ( int b = 0 ; b < this.bdim ; b++ ) {
for ( int y = 0 ; y < this.ydim ; y++ ) {
sum = 0;
for ( int x = 0 ; x < this.xdim ; x++ ) {
sum += input.getPixelXYZTBByte( x,y,0,0,b );
if ( y == 0 ) this.integralImage.setPixelXYZTBInt( x,y,0,0,b, sum );
else this.integralImage.setPixelXYZTBInt( x,y,0,0,b,
this.integralImage.getPixelXYZTBInt( x,y-1,0,0,b ) + sum );
} }
}
}
else if (input instanceof IntegerImage)
{
this.integralImage = new IntegerImage( xdim,ydim,zdim,tdim,bdim );
int sum;
for ( int t = 0 ; t < this.tdim ; t++ )
for ( int z = 0 ; z < this.tdim ; z++ )
for ( int b = 0 ; b < this.bdim ; b++ ) {
for ( int y = 0 ; y < this.ydim ; y++ ) {
sum = 0;
for ( int x = 0 ; x < this.xdim ; x++ ) {
sum += input.getPixelXYZTBInt( x,y,0,0,b );
if ( y == 0 ) this.integralImage.setPixelXYZTBInt( x,y,0,0,b, sum );
else this.integralImage.setPixelXYZTBInt( x,y,0,0,b,
this.integralImage.getPixelXYZTBInt( x,y-1,0,0,b ) + sum );
} }
}
}
else
{
this.integralImage = new DoubleImage( xdim,ydim,zdim,tdim,bdim );
double sum;
for ( int t = 0 ; t < this.tdim ; t++ )
for ( int z = 0 ; z < this.tdim ; z++ )
for ( int b = 0 ; b < this.bdim ; b++ ) {
for ( int y = 0 ; y < this.ydim ; y++ ) {
sum = 0.0;
for ( int x = 0 ; x < this.xdim ; x++ ) {
sum += input.getPixelXYZTBDouble( x,y,0,0,b );
if ( y == 0 ) this.integralImage.setPixelXYZTBDouble( x,y,0,0,b, sum );
else this.integralImage.setPixelXYZTBDouble( x,y,0,0,b,
this.integralImage.getPixelXYZTBDouble( x,y-1,0,0,b ) + sum );
} }
}
}
}
public double area( int x, int y, int width, int height ) {
double[] sums = this.colorArea( x,y, width,height );
return sums[0];
}
public double[] colorArea( int x, int y, int width, int height ) {
return this.colorArea( x,y,0,0, width,height );
}
public double[] colorArea( int x, int y, int z, int t, int width, int height ) {
if ( width <= 0 || height <= 0 )
throw new PelicanException( "Width and height must be strictly positive." );
double []sums = new double[ this.bdim ];
int px = Math.max( x,0 );
int py = Math.max( y,0 );
int pxw = Math.min( x+width,this.xdim ) -1;
int pyh = Math.min( y+height,this.ydim ) -1;
if ( px >= 0 && px < this.xdim
&& py >= 0 && py < this.ydim
&& pxw >= 0 && pxw < this.xdim
&& pyh >= 0 && pyh < this.ydim ) {
if(integralImage instanceof IntegerImage)
{
for ( int i = 0 ; i < this.bdim ; i++ ) {
double a = this.getPixelXYZTBInt( px,py,z,t,i );
double b = this.getPixelXYZTBInt( pxw,py,z,t,i );
double c = this.getPixelXYZTBInt( px,pyh,z,t,i );
double d = this.getPixelXYZTBInt( pxw,pyh,z,t,i );
sums[i] = a+d-(c+b); // = a-c-b+d ... is it faster ?
}
}
else
{
for ( int i = 0 ; i < this.bdim ; i++ ) {
double a = this.getPixelXYZTBDouble( px,py,z,t,i );
double b = this.getPixelXYZTBDouble( pxw,py,z,t,i );
double c = this.getPixelXYZTBDouble( px,pyh,z,t,i );
double d = this.getPixelXYZTBDouble( pxw,pyh,z,t,i );
sums[i] = a+d-(c+b); // = a-c-b+d ... is it faster ?
}
}
} else for ( int i = 0 ; i < this.bdim ; i++ ) sums[i] = 0.0;
return sums;
}
@Override // TODO Auto-generated method stub -lazy programmer !
public Image copyImage( boolean arg0 ) { return null; }
@Override
public boolean equals( Image arg ) {
if ( arg==null || !(arg instanceof IntegralImage) ) return false;
IntegralImage img = ( IntegralImage )arg;
if ( !( this.xdim == img.xdim
&& this.ydim == img.ydim
&& this.zdim == img.zdim
&& this.tdim == img.tdim
&& this.bdim == img.bdim ) ) return false;
int size = this.size();
for ( int i = 0; i < size; i++ )
if ( this.getPixelDouble(i) != img.getPixelDouble(i) ) return false;
return true;
}
@Override
public boolean getPixelBoolean( int p ) { return this.integralImage.getPixelBoolean( p ); }
@Override
public int getPixelByte( int p ) { return this.integralImage.getPixelByte( p ); }
@Override
public double getPixelDouble( int p ) { return this.integralImage.getPixelDouble( p ); }
@Override
public int getPixelInt( int p ) { return this.integralImage.getPixelInt( p ); }
@Override // TODO Auto-generated method stub - me is lazy !
public Image newInstance(int arg0, int arg1, int arg2, int arg3, int arg4) { return null; }
// the "set" methods do nothing for not wrecking the pixel sums.
@Override
public void setPixel( Image arg0,
int arg1, int arg2, int arg3, int arg4, int arg5,
int arg6, int arg7, int arg8, int arg9, int arg10 ) {}
@Override
/** Not allowed. */
public void setPixelBoolean( int p, boolean value ) {}
@Override
/** Not allowed. */
public void setPixelByte( int p, int value ) {}
@Override
/** Not allowed. */
public void setPixelDouble( int p, double value ) {}
@Override
/** Not allowed. */
public void setPixelInt( int p, int value ) {}
@Override
public int size() { return this.integralImage.size(); }
public static final long serialVersionUID = 1L;
}