package cz.cuni.lf1.lge.ThunderSTORM.rendering; import ij.process.ImageProcessor; import static java.lang.Math.ceil; import static cz.cuni.lf1.lge.ThunderSTORM.estimators.PSF.IntegratedSymmetricGaussianPSF.erf; import cz.cuni.lf1.lge.ThunderSTORM.rendering.ui.DensityRenderingUI; /** * This rendering method draws a normalized gaussian blob at every molecule * location. */ public class DensityRendering extends AbstractRendering implements IncrementalRenderingMethod { protected int radius; private DensityRendering(Builder builder) { super(builder); this.radius = builder.radius; } @Override public String getRendererName() { return DensityRenderingUI.name; } public static class Builder extends AbstractBuilder<Builder, DensityRendering> { protected int radius = -1; /** * The radius around the rendered point where pixels are updated (in * pixels). When not set, the value 3*dx (in pixels)is used. * * @param pixels */ public Builder radius(int pixels) { if(radius <= 0) { throw new IllegalArgumentException("Radius must be positive. Passed value = " + pixels); } this.radius = pixels; return this; } @Override public DensityRendering build() { super.validate(); return new DensityRendering(this); } } @Override protected void drawPoint(double x, double y, double z, double dx, double dz) { //3D gaussian blob integrated in over voxel //mathematica function for definite integral: //Integrate[1/(Sqrt[(2*Pi)^3*s1^2*s2^2*s3^2]*E^((1/2)*((x - x0)^2/s1^2 + (y - y0)^2/s2^2 + (z - z0)^2/s3^2))), {z, a, b}, {x, ax, ax + 1}, {y, ay , ay + 1}] if(isInBounds(x, y)) { x = (x - xmin) / resolution; y = (y - ymin) / resolution; dx = dx / resolution; int u = (int) x; int v = (int) y; int actualRadius = (this.radius < 0) ? (int) ceil(dx * 3) : this.radius; double sqrt2dx = Math.sqrt(2) * dx; int w = threeDimensions ? ((int) ((z - zFrom) / zStep)) : 0; int affectedImages = Math.max((int) (3 * dz / zStep), 1); for(int idz = w - affectedImages; idz <= w + affectedImages; idz++) { if(idz >= 0 && idz < zSlices) { double zerfdif; if(threeDimensions) { double aerf = (z - zFrom) - (idz - 1) * zStep; double berf = (z - zFrom) - idz * zStep; zerfdif = (-erf(berf / (Math.sqrt(2) * dz)) + erf(aerf / (Math.sqrt(2) * dz))); } else { zerfdif = 2; } for(int idx = u - actualRadius; idx <= u + actualRadius; idx++) { if(idx >= 0 && idx < imSizeX) { double difx = idx - x; double xerfdif = (erf((difx) / sqrt2dx) - erf((difx + 1) / sqrt2dx)); for(int idy = v - actualRadius; idy <= v + actualRadius; idy++) { if(idy >= 0 && idy < imSizeY) { double dify = idy - y; double val = 0.125 * xerfdif * (erf((dify) / sqrt2dx) - erf((dify + 1) / sqrt2dx)) * zerfdif; ImageProcessor img = slices[idz]; img.setf(idx, idy, (float) val + img.getf(idx, idy)); } } } } } } } } }