%maven net.imagej:ij:1.53s
%maven mpicbg:mpicbg_:1.4.0
import ij.*;
import ij.process.*;
import java.util.*;
import mpicbg.ij.integral.*;
Small experiments for how gradients of spatial frequency filters may be perceived as 3D tilts.
First include ImageJ and the mpicbg library that includes a useful integral image implementation and transformation models:
We want to fill images with white noise, so here are two functions to do so:
void fillWhiteNoise(final FloatProcessor ip) {
final var rnd = new Random(0);
final float[] pixels = (float[])ip.getPixels();
for (int i = 0; i < pixels.length; ++i)
[i] = rnd.nextFloat() * 2 - 1;
pixels}
void fillGaussianNoise(final FloatProcessor ip) {
final var rnd = new Random(0);
final float[] pixels = (float[])ip.getPixels();
for (int i = 0; i < pixels.length; ++i)
[i] = (float)rnd.nextGaussian();
pixels}
This is the Tilt class from mpicbg
but with protected members so we can extend it. It implements a the miniature fake method for photography where the image is blurred with an increasingly smooth kernel whose size depends on the distance from a ‘focal line’ in the image. This blur gradient is parameterized with a line segment that is perpedicular to the ‘focal line’ and whose length defines the speed at which the blur filter size increases:
public class Tilt {
final protected IntegralImage integral;
final protected ImageProcessor ip;
public Tilt(final ColorProcessor ip) {
this.ip = ip;
= new LongRGBIntegralImage(ip);
integral }
public Tilt(final ByteProcessor ip) {
this.ip = ip;
= new LongIntegralImage(ip);
integral }
public Tilt(final ShortProcessor ip) {
this.ip = ip;
= new LongIntegralImage( ip );
integral }
public Tilt(final FloatProcessor ip) {
this.ip = ip;
= new DoubleIntegralImage(ip);
integral }
public void setPixel(
final int x,
final int y,
final int xMin,
final int yMin,
final int xMax,
final int yMax,
final float scale) {
.set(x, y, integral.getScaledSum(xMin, yMin, xMax, yMax, scale));
ip}
public void render(final int x1, final int y1, final int x2, final int y2) {
final int w = ip.getWidth() - 1;
final int h = ip.getHeight() - 1;
final double s = (ip.getWidth() + ip.getHeight()) * 2.0;
final int dx = x2 - x1;
final int dy = y2 - y1;
for (int y = 0; y <= h; ++y) {
final double yt = y - y1;
for (int x = 0; x <= w; ++x) {
final double xt = x - x1;
final double r = (dx * xt + dy * yt) / s;
final int ri = r < 0 ? (int)-r : (int)r;
final int yMin = Math.max(-1, y - ri - 1);
final int yMax = Math.min(h, y + ri);
final int xMin = Math.max(-1, x - ri - 1);
final int xMax = Math.min(w, x + ri);
final float scale = 1.0f / (xMax - xMin) / (yMax - yMin);
setPixel(x, y, xMin, yMin, xMax, yMax, scale);
}
}
}
}
Now a variant of the Tilt class that does not normalize the sum of the blur filter by the number of pixels but by the square root of the number of pixels:
public class Tilt2 extends Tilt {
public Tilt2(final ColorProcessor ip) { super(ip); }
public Tilt2(final ByteProcessor ip) { super(ip); }
public Tilt2(final ShortProcessor ip) { super(ip); }
public Tilt2(final FloatProcessor ip) { super(ip); }
public void setPixel(
final int x,
final int y,
final int xMin,
final int yMin,
final int xMax,
final int yMax,
final float scale) {
.set(x, y, integral.getScaledSum(xMin, yMin, xMax, yMax, (float)Math.sqrt(scale)));
ip// ip.set(x, y, integral.getScaledSum(xMin, yMin, xMax, yMax, 1.0f));
}
}
Let’s make an image and fill it with noise. We create an image that is 10 times larger than what we want to display so we can use area averaging to present a downscaled version with minimal aliasing:
final int scale = 10;
var ip = new FloatProcessor(512 * scale, 512 * scale);
fillWhiteNoise(ip);
.setMinAndMax(-2, 2);
ip.create(ip).scale(1.0 / scale).getBufferedImage(); Scale
Now lets filter this with box filters of increasing size along a vertical gradient:
var ip2 = ip.duplicate();
var tilt2 = new Tilt2((FloatProcessor)ip2);
.render(0, 0, 0, 127);
tilt2.create(ip2).scale(1.0 / scale).getBufferedImage(); Scale
And now the same with a horizontal gradient:
var ip2 = ip.duplicate();
var tilt2 = new Tilt2((FloatProcessor)ip2);
.render(0, 0, 127, 0);
tilt2.create(ip2).scale(1.0 / scale).getBufferedImage(); Scale
Or a bit tilted:
var ip2 = ip.duplicate();
var tilt2 = new Tilt2((FloatProcessor)ip2);
.render(0, 0, 63, 127);
tilt2.create(ip2).scale(1.0 / scale).getBufferedImage(); Scale
var ip = new FloatProcessor(512, 512);
fillWhiteNoise(ip);
= ip.duplicate();
ip2 var mean = Mean.create(ip2);
.mean(2);
meannew ImageJ();
new ImagePlus("noise", ip).show();
new ImagePlus("mean", ip2).show();
= IJ.openImage("https://pixy.org/src2/680/6804381.jpg");
ImagePlus imp var ip = imp.getProcessor().convertToFloatProcessor();
.add(-127);
ip.multiply(1.0 / 127);
ip.setMinAndMax(-2, 2);
ip//ip.getBufferedImage();
var ipScaled = Scale.create(ip).scale(0.4);
.create(ipScaled).scale(0.5).getBufferedImage(); Scale
Error loading image
https://pixy.org/src2/680/6804381.jpg
--------------------------------------------------------------------------- java.lang.NullPointerException: Cannot invoke "ij.ImagePlus.getProcessor()" because "REPL.$JShell$45.imp" is null at .do_it$Aux(#26:1) at .(#26:1)
var ip2 = ipScaled.duplicate();
var tilt2 = new Tilt2((FloatProcessor)ip2);
.render(0, ipScaled.getHeight(), 0, ipScaled.getHeight() - 255);
tilt2.create(ip2).scale(0.5).getBufferedImage(); Scale
| var ip2 = ipScaled.duplicate(); cannot find symbol symbol: variable ipScaled
new ImageJ();
var ip = new FloatProcessor(1014, 1024);
fillGaussianNoise(ip);
new ImagePlus("image", ip).show();
new InteractiveTilt().run("");