package edu.stanford.rsl.tutorial.modelObserver;
import java.util.Random;
import edu.stanford.rsl.conrad.data.numeric.Grid2D;
import edu.stanford.rsl.conrad.data.numeric.NumericPointwiseOperators;
import edu.stanford.rsl.conrad.filtering.LogPoissonNoiseFilteringTool;
import edu.stanford.rsl.tutorial.filters.RamLakKernel;
import edu.stanford.rsl.tutorial.parallel.ParallelBackprojector2D;
import edu.stanford.rsl.tutorial.parallel.ParallelProjector2D;
/**
* @author Iris Kellermann
*/
public class Model extends Grid2D
{
ParallelProjector2D projector;
ParallelBackprojector2D backProjector;
RamLakKernel ramLak;
int PoissonParameter;
public Model(int width, int height)
{
super(width, height);
//parameter which defines the intensity of the Poisson noise (higher -> more noise)
PoissonParameter = 500000;
this.setSpacing(1.0, 1.0);
this.setOrigin(-width/2.d, -height/2.d);
DrawCircle(this, width/2, height/2, width/3, 0.2);
projector = new ParallelProjector2D(Math.PI, Math.PI/180.0, 400, 1);
backProjector = new ParallelBackprojector2D(width, height, 1, 1);
ramLak = new RamLakKernel(400, 1);
}
/**
* Set the type to be used for variation.
*/
public static enum VariationType
{
/** Shifts the object in the image/creates a random salt image */
SimpleVariation,
/** Projects and backprojects the image without using a filter */
ProjectionWOFilter,
/** Projects and backprojects the image and adds Poisson noise */
ProjectionPoisson,
/** Projects and backprojects the image, adds Poisson noise but without intensity shifting */
ProjectionWrongPoisson,
/** Shifts the object in the image and adds noise/creates a random salt image */
SimpleVariationNoise,
/** Projects and backprojects the image without using a filter and adds Poisson noise */
ProjectionPoissonWOFilter
}
/**
* Draws a circle into the given Grid2D with the given radius and the given intensity at the position x,y.
* @param grid The Grid2D to draw the circle in
* @param x The x value of the position.
* @param y The y value of the position.
* @param radius The radius of the circle.
* @param intensity The intensity for the circle.
*/
private void DrawCircle(Grid2D grid, int x, int y, double radius, double intensity)
{
for(int i = 0; i < this.getWidth(); ++i)
{
for(int j = 0; j < this.getHeight(); j++)
{
if((i-x)*(i-x) + (j-y)*(j-y) < (radius * radius))
{
grid.putPixelValue(i,j,intensity);
}
}
}
}
/**
* Creates a Grid2D of a variation of the model.
* @return The image of the model variation.
*/
public Grid2D ModelVariation()
{
int width = this.getWidth();
int height = this.getHeight();
Grid2D newModel = new Grid2D(width, height);
newModel.setSpacing(1.0, 1.0);
newModel.setOrigin(-width/2.d, -height/2.d);
Random rand = new Random();
int random = rand.nextInt(10);
DrawCircle(newModel, newModel.getWidth()/2 + random, newModel.getHeight()/2 + random, newModel.getWidth()/(rand.nextDouble() + 2.5), 1.0);
return newModel;
}
/**
* Creates a new image with Salt noise of arbitrary size.
* @param width The width of the image.
* @param height The height of the image.
* @return The salt image.
*/
public Grid2D CreateSaltImage(int width,int height)
{
Grid2D resultImage = new Grid2D(width, height);
Random rand = new Random();
for(int i = 0; i < width * height / 10; ++i)
{
resultImage.putPixelValue(rand.nextInt(width), rand.nextInt(height), 100*(rand.nextDouble()));
}
resultImage.setSpacing(1.0, 1.0);
resultImage.setOrigin(-width/2.d, -height/2.d);
return resultImage;
}
/**
* Creates a new image with Salt noise of the same size as the model.
* @return The salt image.
*/
public Grid2D CreateSaltImage()
{
return CreateSaltImage(this.getWidth(),this.getHeight());
}
/**
* Creates a new empty image of the same size as the model.
* @return The empty image.
*/
public Grid2D EmptyImage()
{
Grid2D newImage = new Grid2D(this.getWidth(), this.getHeight());
for(int i = 0; i < this.getHeight(); ++i)
{
for(int j = 0; j < this.getWidth(); ++j)
{
newImage.putPixelValue(j, i, 0.0);
}
}
return newImage;
}
/**
* Applies poisson noise to the input sinogram.
* @param sinogram The sinogram to apply the poisson noise.
* @return The resulting sinogram.
*/
public Grid2D PoissonNoise(Grid2D sinogram)
{
Grid2D filteredModel = null;
LogPoissonNoiseFilteringTool poissonFilt = new LogPoissonNoiseFilteringTool();
try {
filteredModel = poissonFilt.applyToolToImage(sinogram);
} catch (Exception e) {
e.printStackTrace();
}
return filteredModel;
}
/**
* Creates a number of different test images with the object present.
* @param number The number of images to create.
* @param type The type of the Variation of the model.
* @return The array of test images.
*/
public Grid2D[] CreateTestModels(int number, VariationType type)
{
Grid2D[] resultArray = new Grid2D[number];
switch(type)
{
case SimpleVariation:
for (int i = 0; i < number; ++i)
{
resultArray[i] = this.ModelVariation();
}
break;
case ProjectionWOFilter:
for (int i = 0; i < number; ++i)
{
//Grid2D modelVariation = this.ModelVariation();
Grid2D modelVariation = this.ModelVariation();
Grid2D sinogram = projector.projectPixelDriven(modelVariation);
//sinogram.show();
NumericPointwiseOperators.addBy(sinogram, this.CreateSaltImage(sinogram.getWidth(),sinogram.getHeight()));
//sinogram.show();
resultArray[i] = backProjector.backprojectPixelDriven(sinogram);
}
break;
case ProjectionPoisson:
for (int i = 0; i < number; ++i)
{
Grid2D sinogram = projector.projectPixelDriven(this.ModelVariation());
NumericPointwiseOperators.divideBy(sinogram, PoissonParameter);
Grid2D poissonSinogram = this.PoissonNoise(sinogram);
NumericPointwiseOperators.multiplyBy(poissonSinogram, PoissonParameter);
Grid2D filteredSinogram = new Grid2D(poissonSinogram);
for (int j = 0; j < filteredSinogram.getSize()[1]; j++)
{
ramLak.applyToGrid(filteredSinogram.getSubGrid(j));
}
resultArray[i] = backProjector.backprojectPixelDriven(filteredSinogram);
}
break;
case ProjectionPoissonWOFilter:
for (int i = 0; i < number; ++i)
{
Grid2D sinogram = projector.projectPixelDriven(this.ModelVariation());
NumericPointwiseOperators.divideBy(sinogram, PoissonParameter);
Grid2D poissonSinogram = this.PoissonNoise(sinogram);
NumericPointwiseOperators.multiplyBy(poissonSinogram, PoissonParameter);
Grid2D filteredSinogram = new Grid2D(poissonSinogram);
resultArray[i] = backProjector.backprojectPixelDriven(filteredSinogram);
}
break;
case ProjectionWrongPoisson:
for (int i = 0; i < number; ++i)
{
Grid2D poissonSinogram = this.PoissonNoise(projector.projectPixelDriven(this.ModelVariation()));
Grid2D filteredSinogram = new Grid2D(poissonSinogram);
for (int j = 0; j < filteredSinogram.getSize()[1]; j++)
{
ramLak.applyToGrid(filteredSinogram.getSubGrid(j));
}
resultArray[i] = backProjector.backprojectPixelDriven(filteredSinogram);
}
break;
case SimpleVariationNoise:
for (int i = 0; i < number; ++i)
{
Grid2D result = this.ModelVariation();
NumericPointwiseOperators.addBy(result, this.CreateSaltImage());
resultArray[i] = result;
}
break;
default:
break;
}
return resultArray;
}
/**
* Creates a number of different empty salt images.
* @param number The number of images to create.
* @param type The type of the variation.
* @return The array of empty images.
*/
public Grid2D[] CreateEmptyImages(int number, VariationType type)
{
Grid2D[] resultArray = new Grid2D[number];
switch(type)
{
case SimpleVariation:
for (int i = 0; i < number; ++i)
{
resultArray[i] = this.CreateSaltImage();
}
break;
case ProjectionWOFilter:
for (int i = 0; i < number; ++i)
{
Grid2D sinogram = projector.projectPixelDriven(this.CreateSaltImage());
resultArray[i] = backProjector.backprojectPixelDriven(sinogram);
}
break;
case ProjectionPoisson:
for (int i = 0; i < number; ++i)
{
Grid2D sinogram = projector.projectPixelDriven(this.CreateSaltImage());
NumericPointwiseOperators.divideBy(sinogram, 40);
Grid2D poissonSinogram = this.PoissonNoise(sinogram);
NumericPointwiseOperators.multiplyBy(poissonSinogram, 40);
Grid2D filteredSinogram = new Grid2D(poissonSinogram);
for (int j = 0; j < filteredSinogram.getSize()[1]; j++)
{
ramLak.applyToGrid(filteredSinogram.getSubGrid(j));
}
resultArray[i] = backProjector.backprojectPixelDriven(filteredSinogram);
}
break;
case ProjectionPoissonWOFilter:
for (int i = 0; i < number; ++i)
{
Grid2D sinogram = projector.projectPixelDriven(this.CreateSaltImage());
NumericPointwiseOperators.divideBy(sinogram, 40);
Grid2D poissonSinogram = this.PoissonNoise(sinogram);
NumericPointwiseOperators.multiplyBy(poissonSinogram, 40);
Grid2D filteredSinogram = new Grid2D(poissonSinogram);
resultArray[i] = backProjector.backprojectPixelDriven(filteredSinogram);
}
break;
case ProjectionWrongPoisson:
for (int i = 0; i < number; ++i)
{
Grid2D poissonSinogram = this.PoissonNoise(projector.projectPixelDriven(this.CreateSaltImage()));
Grid2D filteredSinogram = new Grid2D(poissonSinogram);
for (int j = 0; j < filteredSinogram.getSize()[1]; j++)
{
ramLak.applyToGrid(filteredSinogram.getSubGrid(j));
}
resultArray[i] = backProjector.backprojectPixelDriven(filteredSinogram);
}
break;
case SimpleVariationNoise:
return this.CreateEmptyImages(number, VariationType.SimpleVariation);
}
return resultArray;
}
}
/*
* Copyright (C) 2010-2014 - Iris Kellermann
* CONRAD is developed as an Open Source project under the GNU General Public License (GPL).
*/