/*************************************************************************\
* LoG Filtering Plug-in for Image/J *
* Written by Lokesh Taxali, Email: [email protected] *
* based on algorithm by Dr. Jesse Jin, Email: [email protected] *
* This plug-in is designed to perform LoG Filtering on an 8-bit *
* grayscale image. *
* *
\************************************************************************/
/* Importing standard Java API Files and Image/J packages */
import ij.*;
import ij.plugin.filter.PlugInFilter;
import ij.process.*;
import java.awt.*;
import java.io.*;
import java.lang.*;
import ij.gui.*;
/*
* "Log_Filter" is the main class that implements the plug-in
* 'PlugInFilter' is the Imgage/J interface that has to be
* implemented by this LoG Filtering plug-in
*
* Notes on significance of user input (via GUI) and suggested values:
* Sigma (cfsize) : The width of filter centre (3,5,9,17,35)
* FilterWidth 'w': The width of filter, aka support (2,3,4)
* Threshold 'thr': The threshold for 1 D DoG Filtering
* Delta : The delta level for adjusting zero crossings (-1 .. 1)
* Mode of output : 0: Filtering results in intensity 0 .. 255
* 1: absolute value of filtering
* 2: results representing values -1, 0 or 1
* 3: Zero crossings overlaid on original image
* 4: Zero crossings only
*
*/
public class Log_Filter implements PlugInFilter {
// The following are the input parameters, with default values assigned to them
int cfsize=3;
float w=2 ,delta=0 , thr=0;
int mode=0;
boolean answer;
boolean flag;
//-----------------------------------------------------------------------------------
private boolean GUI()
{
GenericDialog gd = new GenericDialog("Enter Values", IJ.getInstance());
String[] mode_option = {"0","1","2","3","4"};
gd.addNumericField("Sigma (3,5,9,17,35)", cfsize, 0);
gd.addNumericField("FilterWidth (2,3,4)", w, 0);
gd.addNumericField("Threshold (0..0.5)", thr, 1);
gd.addNumericField("Delta (-1..1)", delta, 1);
gd.addChoice("Mode:", mode_option, mode_option[4]);
gd.addMessage("NOTE: Incorrect values entered will be replaced by Default values");
/* Cancel pressed , 0
one or more invalid values , 1
all correct values 2
*/
/* answer =
if(answer == true)
{
return true;
}
else
{
//answer == 2;
return false;
}
*/
return getUserParams(gd);
}
private boolean getUserParams(GenericDialog gd)
{
answer = false;
flag = false;
gd.showDialog();
// The user presses the Cancel button
if (gd.wasCanceled())
{
return false;
}
//-------------------------------------------
cfsize = (int) gd.getNextNumber();
if (gd.invalidNumber())
{
flag = true;
IJ.error("Sigma is invalid.");
}
w = (float) gd.getNextNumber();
if (gd.invalidNumber())
{
flag = true;
IJ.error("Width is invalid.");
}
thr = (float) gd.getNextNumber();
if (gd.invalidNumber())
{
flag = true;
IJ.error("Threshold is invalid.");
}
delta = (float)gd.getNextNumber();
if (gd.invalidNumber())
{
flag = true;
IJ.error("Delta is invalid.");
}
mode = gd.getNextChoiceIndex();
if (gd.invalidNumber())
{
flag = true;
IJ.error("Mode is invalid.");
}
if (flag == true)
{
answer = getUserParams(gd);
}
else
{
answer = true;
}
return answer;
}// end of method getUserParams
//--------------------------------------------------------------------------------------------------
/* This method is used to process the image. Since the
SUPPORTS_STACKS flag was set, it is called for each slice in
a stack. Image/J will lock the image before calling
this method and unlock it when the filter is finished.
Abstract Superclass 'ImageProcessor' represents the type of
image the current image is and provides utility methods for it
*/
public void run(ImageProcessor ip)
{
if(GUI() == false)
{
return;
}
else
{
// 'csize' is the height of the image
int csize = ip.getHeight();
// 'rsize' is the width of the image
int rsize = ip.getWidth();
// 'Rectangle' represents the co-ordinates of the smallest
// enclosing rectangle for the user selected region (ROI)
Rectangle rect = ip.getRoi();
// General use variables
int d0,a0,acr,dow,it;
int i,x,y;
double h12, h21, ft, h1h2, h2h1, fmu, dh, dv;
double r, dt, dmx, dmn;
// 1D LoG function
float logaus[] = new float[(rect.width>rect.height)? rect.width : rect.height];
// 1D Gaussian function
float gaus[] = new float[(rect.width>rect.height)? rect.width : rect.height];
// 1 D DoG function (Difference of Gaussian)
float dgaus[] = new float[(rect.width>rect.height)? rect.width : rect.height];
// represents the number of ZCs found
long zcn =0;
// 'ip.getPixels()' returns the image's pixels in an array
// Since this filter only deals with 8-bit grayscale images, the array
// type is 'byte'
byte pixels[] = (byte[])ip.getPixels();
// The array 'img_in' stores the pixel values for input image in
// decimal format, in the range 0 - 255
int img_in[] = new int[rect.width*rect.height];
// The following will replace the user values which are out of an allowable range
if (cfsize<0) cfsize=3;
if (cfsize>35) cfsize=35;
if(w<0) w=0;
// Can add more conditions here on the input parameters to restrict parameters' range
IJ.write("");
IJ.write("cfise is= " + cfsize);
IJ.write("w is= " + w);
IJ.write("thr is= " + thr);
IJ.write("delta is= " + delta);
IJ.write("mode is= " + mode );
// Calculating the Filter's size
int fsize = (int)(cfsize*w);
if (fsize%2 == 0)
{
fsize += 1;
}
IJ.write("The filter size = "+ fsize);
// These two are temporary storage arrays for the image during processing
// They are only required to be as big as the ROI, because only the ROI is being processed
double dimg[] = new double[rect.height*rect.width];
double dr[] = new double[rect.height*rect.width];
i=0;
// The following loop converts the byte values in the 'pixels' array to int values in the
// range 0 to 255, and copies them to the array 'img_in'
// Note that only the pixels in the ROI are copied to the destination array, because
// they are the only ones that need to be processed
for(y=rect.y;y<(rect.y+rect.height);y++)
{
for(x=rect.x;x<(rect.x+rect.width);x++)
{
img_in[i] = (pixels[(y*rsize)+x]&0xff);
i++;
}
}
IJ.write("The dimensions of smallest enclosing rectangle are: ");
IJ.write("rect.y= "+rect.y+"; rect.x= "+rect.x);
IJ.write("rect.width= "+rect.width+"; rect.height= "+rect.height);
// 'size' is the extended row size (by fsize/2)
int size = rect.width + fsize -1;
// The array 'image' is an array extended by (fsize/2) in both dimensions, to hold
// the processed image (during convolution)
int image[] = new int[(rect.width+fsize-1)*(rect.height+fsize-1)];
int extension= (fsize/2);
// The pixel values from the 'img_in' array are copied to the centre of the extended
// array 'image'
for( i=0; i<rect.height;i++)
{
System.arraycopy(img_in,(i*rect.width),image,( ((i+extension)*(rect.width+fsize-1))+ extension ),rect.width);
}
// Initialise variables
h1h2= h2h1 = h12 =0.0;
// The following sets the filter into 'logaus[]' and sum(h12+h21)
for(i=1; i<( (fsize+1) /2);i++)
{
w = (float)cfsize/(float)2.0/(float)1.414;
IJ.write("w="+w);
ft = i/w;
IJ.write("ft="+ft);
gaus[i] = (float)Math.exp(-ft*ft/2);
IJ.write("gaus[i]="+gaus[i]);