package com.elphel.imagej.calibration;
/**
**
** MatchSimulatedPattern.java - Determine simulation pattern parameters to match
** the acquired image
**
** Copyright (C) 2010-2014 Elphel, Inc.
**
** -----------------------------------------------------------------------------**
**
** MatchSimulatedPattern.java is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see .
** -----------------------------------------------------------------------------**
**
*/
import java.awt.Point;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import javax.swing.SwingUtilities;
import com.elphel.imagej.common.DoubleFHT;
import com.elphel.imagej.common.DoubleGaussianBlur;
import com.elphel.imagej.common.PolynomialApproximation;
import com.elphel.imagej.common.ShowDoubleFloatArrays;
import com.elphel.imagej.common.WindowTools;
import com.elphel.imagej.jp4.JP46_Reader_camera;
import com.elphel.imagej.lwir.LwirReaderParameters;
import Jama.LUDecomposition;
import Jama.Matrix; // Download here: http://math.nist.gov/javanumerics/jama/
import ij.IJ;
import ij.ImagePlus;
import ij.ImageStack;
import ij.gui.GenericDialog;
import ij.gui.PointRoi;
import ij.gui.Roi;
import ij.process.FHT; // get rid, change to double
import ij.process.FloatProcessor;
import ij.process.ImageProcessor;
public class MatchSimulatedPattern {
public int debugLevel = 2;
public int FFT_SIZE = 256;
public double[][][][] PATTERN_GRID = null; // global to be used with threads? TODO: Same as DIST_ARRAY - merge?
public int[] minUV = { 0, 0 };
public int[][] reMap = null; // maps grid coordinates from laser pointers to PATTERN_GRID u,v (2x3 - rotation
// + translation) - SEEMS NOT USED!
public int[] UVShiftRot = { 0, 0, 0 }; // {shift U, shift V, rot (1 of 8 - 4 non-mirrored, 4 - mirrored}
public int[][][] targetUV = null; // maps PATTERN_GRID cells to the target (absolute) UV
// public double [][][] pixelsUV=null; // made of PATTERN_GRID, but does not
// have any wave vectors. Calculated during laser calibration
public double[][][] pXYUV = null; // made of PATTERN_GRID, but does not have any wave vectors. Calculated during
// laser calibration
public double[][][] gridContrastBrightness = null; // {grid contrast, grid intensity red, grid intensity green, grid
// intensity blue}[v][u]
public Rectangle DIST_SELECTION = null;
public int[] UV_INDEX = null; // array containing index of the pattern UV (scanline order, U first), or -1 for
// the areas with no pattern
public int UV_INDEX_WIDTH = 0; // width of UV_INDEX (== full image, not selection, width)
public int[] debugUV = { -1, -1 }; // debug the same cell on the second pass
public int passNumber = 0;
public boolean[] correlationSizesUsed = null;
public double[] gridFFCorr = null; // array matching greens with the flat field correction for the grid (zero
// outside of detected grid?)
public double[] flatFieldForGrid = null; // array matching image pixels, divide the input pixels by these values (if
// not null)
public boolean[] focusMask = null; // array matching image pixels, used with focusing (false outside sample areas)
final private static int[][][] rotations = { { { 1, 0 }, { 0, 1 } }, // not mirrored
{ { 0, 1 }, { -1, 0 } }, { { -1, 0 }, { 0, -1 } }, { { 0, -1 }, { 1, 0 } },
{ { 1, 0 }, { 0, -1 } }, // mirrored
{ { 0, 1 }, { 1, 0 } }, { { -1, 0 }, { 0, 1 } }, { { 0, -1 }, { -1, 0 } } };
// shifts when rotating around unknown center (make it white)
final private static int[][] dfltShifts = { { 0, 0 }, { 0, 1 }, { 0, 0 }, { 1, 0 }, { 0, 1 }, { 0, 0 }, { 1, 0 },
{ 0, 0 } };
final private static int[][] combinedRotations = { { 0, 1, 2, 3, 4, 5, 6, 7 }, { 1, 2, 3, 0, 7, 4, 5, 6 },
{ 2, 3, 0, 1, 6, 7, 4, 5 }, { 3, 0, 1, 2, 5, 6, 7, 4 }, { 4, 5, 6, 7, 0, 1, 2, 3 },
{ 5, 6, 7, 4, 3, 0, 1, 2 }, { 6, 7, 4, 5, 2, 3, 0, 1 }, { 7, 4, 5, 6, 1, 2, 3, 0 } };
public MatchSimulatedPattern() {
}
// public MatchSimulatedPattern(int fft_size) {
// this.FFT_SIZE = fft_size;
// }
public MatchSimulatedPattern(int fft_size) {
this.FFT_SIZE = fft_size;
}
// not real clone, just for threads - if there will be FFT - keep individual
@Override
public MatchSimulatedPattern clone() { // used in createPSFMap when creating threads
MatchSimulatedPattern msp = new MatchSimulatedPattern(this.FFT_SIZE);
// cloning should be thread safe, when using DoubleFHT - use individual
// instances
msp.debugLevel = this.debugLevel;
msp.PATTERN_GRID = this.PATTERN_GRID; // global to be used with threads? TODO: Same as DIST_ARRAY - merge?
// msp.DIST_ARRAY=this.DIST_ARRAY;
msp.DIST_SELECTION = this.DIST_SELECTION;
msp.UV_INDEX = this.UV_INDEX; // array containing index of the pattern UV (scanline order, U first), or -1 for
// the areas with no pattern
msp.UV_INDEX_WIDTH = this.UV_INDEX_WIDTH;
msp.reMap = this.reMap;
msp.UVShiftRot = this.UVShiftRot.clone();
// public int [] UVShiftRot={0,0,0}; // {shift U, shift V, rot (1 of 8 - 4
// non-mirrored, 4 - mirrored}
msp.targetUV = this.targetUV; //
msp.pXYUV = this.pXYUV;
msp.flatFieldForGrid = this.flatFieldForGrid;
msp.focusMask = this.focusMask;
return msp;
}
public MatchSimulatedPattern cloneDeep(boolean clonePATTERN_GRID, boolean cloneTargetUV, boolean clonePixelsUV,
boolean cloneFlatFieldForGrid, boolean cloneFocusMask) { // used in createPSFMap when creating threads
MatchSimulatedPattern msp = new MatchSimulatedPattern(this.FFT_SIZE);
// cloning should be thread safe, when using DoubleFHT - use individual
// instances
msp.debugLevel = this.debugLevel;
// msp.PATTERN_GRID=this.PATTERN_GRID; // global to be used with threads? TODO:
// Same as DIST_ARRAY - merge?
if (clonePATTERN_GRID && (msp.PATTERN_GRID != null)) {
msp.PATTERN_GRID = new double[this.PATTERN_GRID.length][this.PATTERN_GRID[0].length][][];
for (int i = 0; i < this.PATTERN_GRID.length; i++)
for (int j = 0; j < this.PATTERN_GRID[i].length; j++) {
if (this.PATTERN_GRID[i][j] != null) {
msp.PATTERN_GRID[i][j] = new double[this.PATTERN_GRID[i][j].length][];
for (int k = 0; k < this.PATTERN_GRID[i][j].length; k++) {
if (this.PATTERN_GRID[i][j][k] != null)
msp.PATTERN_GRID[i][j][k] = this.PATTERN_GRID[i][j][k].clone();
else
msp.PATTERN_GRID[i][j][k] = null;
}
} else
msp.PATTERN_GRID[i][j] = null;
}
} else
msp.PATTERN_GRID = this.PATTERN_GRID;
// msp.DIST_ARRAY=this.DIST_ARRAY;
if (this.DIST_SELECTION != null)
msp.DIST_SELECTION = new Rectangle(this.DIST_SELECTION);
else
msp.DIST_SELECTION = null;
if (this.UV_INDEX != null)
msp.UV_INDEX = this.UV_INDEX.clone(); // array containing index of the pattern UV (scanline order, U first),
// or -1 for the areas with no pattern
else
msp.UV_INDEX = null;
msp.UV_INDEX_WIDTH = this.UV_INDEX_WIDTH;
if (this.reMap != null) { // probably not used
msp.reMap = new int[2][];
msp.reMap[0] = this.reMap[0].clone();
msp.reMap[1] = this.reMap[1].clone();
} else
msp.reMap = null;
msp.UVShiftRot = this.UVShiftRot.clone();
// public int [] UVShiftRot={0,0,0}; // {shift U, shift V, rot (1 of 8 - 4
// non-mirrored, 4 - mirrored}
// msp.targetUV=this.targetUV;
if (cloneTargetUV && (this.targetUV != null)) {
msp.targetUV = new int[this.targetUV.length][this.targetUV[0].length][];
for (int i = 0; i < this.targetUV.length; i++)
for (int j = 0; j < this.targetUV[i].length; j++) {
if (this.targetUV[i][j] != null)
msp.targetUV[i][j] = this.targetUV[i][j].clone();
else
msp.targetUV[i][j] = null;
}
} else
msp.targetUV = this.targetUV;
// msp.pixelsUV=this.pixelsUV;
if (clonePixelsUV && (this.pXYUV != null)) {
msp.pXYUV = new double[this.pXYUV.length][this.pXYUV[0].length][];
for (int i = 0; i < this.pXYUV.length; i++)
for (int j = 0; j < this.pXYUV[i].length; j++) {
if (this.pXYUV[i][j] != null)
msp.pXYUV[i][j] = this.pXYUV[i][j].clone();
else
msp.pXYUV[i][j] = null;
}
} else
msp.targetUV = this.targetUV;
// msp.flatFieldForGrid=this.flatFieldForGrid;
if (cloneFlatFieldForGrid && (this.flatFieldForGrid != null))
msp.flatFieldForGrid = this.flatFieldForGrid.clone();
else
msp.flatFieldForGrid = this.flatFieldForGrid;
// msp.focusMask=this.focusMask;
if (cloneFocusMask && (this.focusMask != null))
msp.focusMask = this.focusMask.clone();
else
msp.focusMask = this.focusMask;
return msp;
}
public int[] getUVShiftRot(boolean shift) {
if (!shift)
return this.UVShiftRot;
int[][] reReMap = getRemapMatrix(this.UVShiftRot);
int[] UVShiftRotCorr = this.UVShiftRot.clone();
UVShiftRotCorr[0] -= reReMap[0][0] * this.minUV[0] + reReMap[0][1] * this.minUV[1];
UVShiftRotCorr[1] -= reReMap[1][0] * this.minUV[0] + reReMap[1][1] * this.minUV[1];
System.out.println("getUVShiftRot(true): minUV[0]=" + minUV[0] + " minUV[1]=" + minUV[1]);
return UVShiftRotCorr;
}
public static int[][] getRemapMatrix(int[] UVShiftRot) {
// Moved shift calculation to calibrateGrid(), here just a regular R,T 2x3 matix
int[][] reReMap = { { rotations[UVShiftRot[2]][0][0], rotations[UVShiftRot[2]][0][1], UVShiftRot[0] },
{ rotations[UVShiftRot[2]][1][0], rotations[UVShiftRot[2]][1][1], UVShiftRot[1] } };
return reReMap;
}
public static int[] combineUVShiftRot(int[] UVShiftRotA, int[] UVShiftRotB) {
int[][] reReMapA = getRemapMatrix(UVShiftRotA);
int[][] reReMapB = getRemapMatrix(UVShiftRotB);
int[] UVShiftRot = new int[3];
UVShiftRot[0] = reReMapB[0][0] * reReMapA[0][2] + reReMapB[0][1] * reReMapA[1][2] + reReMapB[0][2];
UVShiftRot[1] = reReMapB[1][0] * reReMapA[0][2] + reReMapB[1][1] * reReMapA[1][2] + reReMapB[1][2];
UVShiftRot[2] = combinedRotations[UVShiftRotA[2]][UVShiftRotB[2]];
return UVShiftRot;
}
public double focusQualityR2(ImagePlus imp, int fftSize, int spotSize, // 5
double x0, double y0, int debugLevel) {
int ix0 = (int) Math.round(x0);
int iy0 = (int) Math.round(y0);
Rectangle selection = new Rectangle(ix0 - fftSize, iy0 - fftSize, fftSize * 2, fftSize * 2);
double[][] pixels = splitBayer(imp, selection, true);
// ShowDoubleFloatArrays.showArrays(pixels, fftSize, fftSize, true,"bayer");
DoubleFHT fht_instance = new DoubleFHT(); // provide DoubleFHT instance to save on initializations (or null)
double[] hamming1d = fht_instance.getHamming1d(fftSize);
for (int c = 0; c < pixels.length; c++)
if (pixels[c] != null) {
double sum = 0.0;
for (int i = 0; i < pixels[c].length; i++)
sum += pixels[c][i];
double average = sum / pixels[c].length;
int index = 0;
for (int y = 0; y < fftSize; y++)
for (int x = 0; x < fftSize; x++) {
pixels[c][index] = (pixels[c][index] - average) * hamming1d[y] * hamming1d[x];
index++;
}
}
// slightly blur the image to be sure there are no aliases in the low
// frequencies
double preBlurSigma = 1.5;
DoubleGaussianBlur gb = new DoubleGaussianBlur();
for (int c = 0; c < pixels.length; c++)
if (pixels[c] != null) {
gb.blurDouble(pixels[c], fftSize, fftSize, preBlurSigma, preBlurSigma, 0.01);
}
// ShowDoubleFloatArrays.showArrays(pixels, fftSize, fftSize, true,"bayer-winowed");
for (int c = 0; c < pixels.length; c++)
if (pixels[c] != null) {
fht_instance.swapQuadrants(pixels[c]);
fht_instance.transform(pixels[c]);
pixels[c] = fht_instance.calculateAmplitude(pixels[c]);
}
if (debugLevel > 1)
ShowDoubleFloatArrays.showArrays(pixels, fftSize, fftSize, true, "amplitudes");
int[][][] spectrumMaximums = new int[pixels.length][][];
double[] spectralContrast = new double[pixels.length];
int hsize = fftSize / 2;
int radius = spotSize / 2; // 2
int[][] dirs = { { -1, 0 }, { -1, 1 }, { 0, 1 }, { 1, 1 }, { 1, 0 }, { 1, -1 }, { 0, -1 }, { -1, -1 } };
int lowLim = (fftSize * 3) / 8;
int highLim = (fftSize * 5) / 8;
for (int c = 0; c < pixels.length; c++)
if (pixels[c] != null) {
spectrumMaximums[c] = new int[2][2];
double max = 0.0;
for (int y = lowLim; y <= fftSize / 2; y++)
for (int x = lowLim; x < highLim; x++) {
if ((y >= (hsize - radius)) && (x >= (hsize - radius)) && (x <= (hsize + radius)))
continue; // do not count zero freq
if (max < pixels[c][y * fftSize + x]) {
max = pixels[c][y * fftSize + x];
spectrumMaximums[c][0][0] = x;
spectrumMaximums[c][0][1] = y;
}
}
max = 0.0;
for (int y = lowLim; y <= fftSize / 2; y++)
for (int x = lowLim; x < highLim; x++) {
if ((y >= (hsize - radius)) && (x >= (hsize - radius)) && (x <= (hsize + radius))) {
if ((debugLevel > 1) && (c == 0))
System.out.println("x]=" + x + " y=" + y + " - too close to 0,0");
continue; // do not count zero freq
}
if ((y >= (spectrumMaximums[c][0][1] - radius)) && (y <= (spectrumMaximums[c][0][1] + radius))
&& (x >= (spectrumMaximums[c][0][0] - radius))
&& (x <= (spectrumMaximums[c][0][0] + radius))) {
if ((debugLevel > 1) && (c == 0))
System.out.println("x=" + x + " y=" + y + " - too close to first max "
+ spectrumMaximums[c][0][0] + ":" + spectrumMaximums[c][0][1]);
continue; // do not count zero freq
}
if ((y >= ((fftSize - spectrumMaximums[c][0][1]) - radius))
&& (y <= ((fftSize - spectrumMaximums[c][0][1]) + radius))
&& (x >= ((fftSize - spectrumMaximums[c][0][0]) - radius))
&& (x <= ((fftSize - spectrumMaximums[c][0][0]) + radius))) {
if ((debugLevel > 1) && (c == 0))
System.out.println("x=" + x + " y=" + y + " - too close to alias "
+ (fftSize - spectrumMaximums[c][0][0]) + ":"
+ (fftSize - spectrumMaximums[c][0][1]));
continue; // do not count zero first maximum
}
if (max < pixels[c][y * fftSize + x]) {
max = pixels[c][y * fftSize + x];
spectrumMaximums[c][1][0] = x;
spectrumMaximums[c][1][1] = y;
if ((debugLevel > 1) && (c == 0))
System.out.println("New max at x=" + x + " y=" + y + ": " + max);
}
}
if (debugLevel > 1)
System.out.println("spectrumMaximums[" + c + "]=" + (spectrumMaximums[c][0][0] - hsize) + ":"
+ (spectrumMaximums[c][0][1] - hsize) + ", " + "" + (spectrumMaximums[c][1][0] - hsize)
+ ":" + (spectrumMaximums[c][1][1] - hsize));
// double s1=0,s2=0;
int[][] xy3 = { { 3 * spectrumMaximums[c][0][0] - fftSize, 3 * spectrumMaximums[c][0][1] - fftSize },
{ 3 * spectrumMaximums[c][1][0] - fftSize, 3 * spectrumMaximums[c][1][1] - fftSize } };
// if (debugLevel>3){
if ((c == 0) && (debugLevel > 1)) {
System.out.println(" xy3[0][0]=" + xy3[0][0] + " xy3[0][1]=" + xy3[0][1]);
System.out.println(" xy3[1][0]=" + xy3[1][0] + " xy3[1][1]=" + xy3[1][1]);
}
// make 3-rd harmonic frequency adjustment
// May be improved by quadratic maximums
for (int n = 0; n < 2; n++)
for (int i = 0; i < 2; i++) {
max = pixels[c][xy3[i][1] * fftSize + xy3[i][0]];
int d = -1;
for (int dir = 0; dir < dirs.length; dir++) {
double v = pixels[c][(xy3[i][1] + dirs[dir][1]) * fftSize + (xy3[i][0] + dirs[dir][0])];
if (max < v) {
max = v;
d = dir;
}
}
if (d >= 0) {
xy3[i][0] += dirs[d][0];
xy3[i][1] += dirs[d][1];
}
}
// if (debugLevel>2) {
if ((c == 0) && (debugLevel > 1)) {
System.out.println("*xy3[0][0]=" + xy3[0][0] + " xy3[0][1]=" + xy3[0][1]);
System.out.println("*xy3[1][0]=" + xy3[1][0] + " xy3[1][1]=" + xy3[1][1]);
}
double[][] xy = { { (xy3[0][0] - hsize) / 3.0, (xy3[0][1] - hsize) / 3.0 },
{ (xy3[1][0] - hsize) / 3.0, (xy3[1][1] - hsize) / 3.0 } };
if ((c == 0) && (debugLevel > 1)) {
System.out.println(" xy[0][0]=" + IJ.d2s(xy[0][0], 1) + " xy3[0][1]=" + IJ.d2s(xy[0][1], 1));
System.out.println(" xy[1][0]=" + IJ.d2s(xy[1][0], 1) + " xy3[1][1]=" + IJ.d2s(xy[1][1], 1));
}
// once more - correct 5-th:
int[][] xy5 = { { hsize + (int) Math.round(5 * xy[0][0]), hsize + (int) Math.round(5 * xy[0][1]) },
{ hsize + (int) Math.round(5 * xy[1][0]), hsize + (int) Math.round(5 * xy[1][1]) } };
// just once
if ((c == 0) && (debugLevel > 1)) {
System.out.println(" xy5[0][0]=" + xy5[0][0] + " xy5[0][1]=" + xy5[0][1]);
System.out.println(" xy5[1][0]=" + xy5[1][0] + " xy5[1][1]=" + xy5[1][1]);
}
for (int i = 0; i < 2; i++) {
max = pixels[c][xy5[i][1] * fftSize + xy5[i][0]];
int d = -1;
for (int dir = 0; dir < dirs.length; dir++) {
double v = pixels[c][(xy5[i][1] + dirs[dir][1]) * fftSize + (xy5[i][0] + dirs[dir][0])];
if (max < v) {
max = v;
d = dir;
}
}
if (d >= 0) {
xy5[i][0] += dirs[d][0];
xy5[i][1] += dirs[d][1];
}
}
if ((c == 0) && (debugLevel > 1)) {
System.out.println("*xy5[0][0]=" + xy5[0][0] + " xy5[0][1]=" + xy5[0][1]);
System.out.println("*xy5[1][0]=" + xy5[1][0] + " xy5[1][1]=" + xy5[1][1]);
}
for (int i = 0; i < 2; i++)
for (int j = 0; j < 2; j++)
xy[i][j] = (xy5[i][j] - hsize) / 5.0;
if ((c == 0) && (debugLevel > 1)) {
System.out.println(" xy[0][0]=" + IJ.d2s(xy[0][0], 1) + " xy3[0][1]=" + IJ.d2s(xy[0][1], 1));
System.out.println(" xy[1][0]=" + IJ.d2s(xy[1][0], 1) + " xy3[1][1]=" + IJ.d2s(xy[1][1], 1));
}
// now generate mask and then blur it
int maxMode = 7; // 1, 3-rd and 5-th harmonics, should be odd (now 7-th included)
double[][] dirNeg = { { -0.5, -0.5 }, { 0.5, -0.5 }, { -0.5, 0.5 }, { 0.5, 0.5 } }; // direction to
// negative mask
double[] mask = new double[fftSize * fftSize];
for (int i = 0; i < mask.length; i++)
mask[i] = 0.0;
for (int i = -maxMode; i <= maxMode; i += 2)
for (int j = -maxMode; j <= maxMode; j += 2) {
double xpc = -0.5 * (xy[0][0] * (i + j) + xy[1][0] * (i - j));
double ypc = -0.5 * (xy[0][1] * (i + j) + xy[1][1] * (i - j));
double xp = xpc + hsize;
double yp = ypc + hsize;
double w = xpc * xpc + ypc * ypc; // increase weight of high frequencies
int ixp = (int) Math.round(xp);
int iyp = (int) Math.round(yp);
if ((ixp < 0) || (ixp >= fftSize) || (iyp < 0) || (iyp >= fftSize))
continue;
mask[iyp * fftSize + ixp] += w;
if ((c == 0) && (debugLevel > 1))
System.out.println(" xp=" + IJ.d2s(xp, 1) + " xm=" + IJ.d2s(yp, 1));
for (int d = 0; d < dirNeg.length; d++) {
double xm = xp + dirNeg[d][0] * xy[0][0] + dirNeg[d][1] * xy[1][0];
double ym = yp + dirNeg[d][0] * xy[0][1] + dirNeg[d][1] * xy[1][1];
int ixm = (int) Math.round(xm);
int iym = (int) Math.round(ym);
ixm = (ixm + fftSize) % fftSize;
iym = (iym + fftSize) % fftSize;
mask[iym * fftSize + ixm] -= w / dirNeg.length;
}
}
double sigmaScale = 0.2;
double averageLength = Math.sqrt(
(xy[0][0] * xy[0][0] + xy[0][1] * xy[0][1] + xy[1][0] * xy[1][0] + xy[1][1] * xy[1][1]) / 2);
if ((c == 0) && (debugLevel > 1))
ShowDoubleFloatArrays.showArrays(mask, fftSize, fftSize, "mask_color");
gb.blurDouble(mask, fftSize, fftSize, sigmaScale * averageLength, sigmaScale * averageLength, 0.01);
if ((c == 0) && (debugLevel > 1))
ShowDoubleFloatArrays.showArrays(mask, fftSize, fftSize,
"mask_color_blured" + IJ.d2s(sigmaScale * averageLength, 3));
double SFR2 = 0.0, SFP = 0.0;
for (int i = 0; i < mask.length; i++) {
int x = (i % fftSize) - hsize;
int y = (i / fftSize) - hsize;
SFR2 += pixels[c][i] * mask[i] * (x * x + y * y);
if (mask[i] > 0)
SFP += pixels[c][i] * mask[i]; // sum only positive masks? Or abs value? Does not really matter,
// it is just a scale
}
spectralContrast[c] = Math.sqrt(SFR2 / SFP) / averageLength;
if ((c == 0) && (debugLevel > 1))
System.out.println("SFR2=" + SFR2 + " SFP=" + SFP + " averageLength=" + averageLength);
if ((debugLevel > 1))
System.out.println("spectrumContrast[" + c + "]=" + spectralContrast[c]);
} else {
spectrumMaximums[c] = null;
spectralContrast[c] = Double.NaN;
}
// return
// 0.25*(spectralContrast[0]+spectralContrast[1]+spectralContrast[2]+spectralContrast[3]);
return 0.5 * (spectralContrast[0] + spectralContrast[3]); // green only
}
public double focusQuality(ImagePlus imp, int fftSize, int spotSize, // 5
double x0, double y0, int debugLevel) {
int ix0 = (int) Math.round(x0);
int iy0 = (int) Math.round(y0);
Rectangle selection = new Rectangle(ix0 - fftSize, iy0 - fftSize, fftSize * 2, fftSize * 2);
double[][] pixels = splitBayer(imp, selection, true);
// ShowDoubleFloatArrays.showArrays(pixels, fftSize, fftSize, true,"bayer");
DoubleFHT fht_instance = new DoubleFHT(); // provide DoubleFHT instance to save on initializations (or null)
double[] hamming1d = fht_instance.getHamming1d(fftSize);
for (int c = 0; c < pixels.length; c++)
if (pixels[c] != null) {
double sum = 0.0;
for (int i = 0; i < pixels[c].length; i++)
sum += pixels[c][i];
double average = sum / pixels[c].length;
int index = 0;
for (int y = 0; y < fftSize; y++)
for (int x = 0; x < fftSize; x++) {
pixels[c][index] = (pixels[c][index] - average) * hamming1d[y] * hamming1d[x];
index++;
}
}
// slightly blur the image to be sure there are no aliases in the low
// frequencies
double preBlurSigma = 1.5;
DoubleGaussianBlur gb = new DoubleGaussianBlur();
for (int c = 0; c < pixels.length; c++)
if (pixels[c] != null) {
gb.blurDouble(pixels[c], fftSize, fftSize, preBlurSigma, preBlurSigma, 0.01);
}
// ShowDoubleFloatArrays.showArrays(pixels, fftSize, fftSize, true,"bayer-winowed");
for (int c = 0; c < pixels.length; c++)
if (pixels[c] != null) {
fht_instance.swapQuadrants(pixels[c]);
fht_instance.transform(pixels[c]);
pixels[c] = fht_instance.calculateAmplitude(pixels[c]);
}
if (debugLevel > 1)
ShowDoubleFloatArrays.showArrays(pixels, fftSize, fftSize, true, "amplitudes");
int[][][] spectrumMaximums = new int[pixels.length][][];
double[] spectralContrast = new double[pixels.length];
int hsize = fftSize / 2;
int radius = spotSize / 2; // 2
int[][] dirs = { { -1, 0 }, { -1, 1 }, { 0, 1 }, { 1, 1 }, { 1, 0 }, { 1, -1 }, { 0, -1 }, { -1, -1 } };
int lowLim = (fftSize * 3) / 8;
int highLim = (fftSize * 5) / 8;
for (int c = 0; c < pixels.length; c++)
if (pixels[c] != null) {
spectrumMaximums[c] = new int[2][2];
double max = 0.0;
for (int y = lowLim; y <= fftSize / 2; y++)
for (int x = lowLim; x < highLim; x++) {
if ((y >= (hsize - radius)) && (x >= (hsize - radius)) && (x <= (hsize + radius)))
continue; // do not count zero freq
if (max < pixels[c][y * fftSize + x]) {
max = pixels[c][y * fftSize + x];
spectrumMaximums[c][0][0] = x;
spectrumMaximums[c][0][1] = y;
}
}
max = 0.0;
for (int y = lowLim; y <= fftSize / 2; y++)
for (int x = lowLim; x < highLim; x++) {
if ((y >= (hsize - radius)) && (x >= (hsize - radius)) && (x <= (hsize + radius))) {
if ((debugLevel > 1) && (c == 0))
System.out.println("x]=" + x + " y=" + y + " - too close to 0,0");
continue; // do not count zero freq
}
if ((y >= (spectrumMaximums[c][0][1] - radius)) && (y <= (spectrumMaximums[c][0][1] + radius))
&& (x >= (spectrumMaximums[c][0][0] - radius))
&& (x <= (spectrumMaximums[c][0][0] + radius))) {
if ((debugLevel > 1) && (c == 0))
System.out.println("x=" + x + " y=" + y + " - too close to first max "
+ spectrumMaximums[c][0][0] + ":" + spectrumMaximums[c][0][1]);
continue; // do not count zero freq
}
if ((y >= ((fftSize - spectrumMaximums[c][0][1]) - radius))
&& (y <= ((fftSize - spectrumMaximums[c][0][1]) + radius))
&& (x >= ((fftSize - spectrumMaximums[c][0][0]) - radius))
&& (x <= ((fftSize - spectrumMaximums[c][0][0]) + radius))) {
if ((debugLevel > 1) && (c == 0))
System.out.println("x=" + x + " y=" + y + " - too close to alias "
+ (fftSize - spectrumMaximums[c][0][0]) + ":"
+ (fftSize - spectrumMaximums[c][0][1]));
continue; // do not count zero first maximum
}
if (max < pixels[c][y * fftSize + x]) {
max = pixels[c][y * fftSize + x];
spectrumMaximums[c][1][0] = x;
spectrumMaximums[c][1][1] = y;
if ((debugLevel > 1) && (c == 0))
System.out.println("New max at x=" + x + " y=" + y + ": " + max);
}
}
if (debugLevel > 1)
System.out.println("spectrumMaximums[" + c + "]=" + (spectrumMaximums[c][0][0] - hsize) + ":"
+ (spectrumMaximums[c][0][1] - hsize) + ", " + "" + (spectrumMaximums[c][1][0] - hsize)
+ ":" + (spectrumMaximums[c][1][1] - hsize));
// double s1=0,s2=0;
int[][] xy3 = { { 3 * spectrumMaximums[c][0][0] - fftSize, 3 * spectrumMaximums[c][0][1] - fftSize },
{ 3 * spectrumMaximums[c][1][0] - fftSize, 3 * spectrumMaximums[c][1][1] - fftSize } };
// if (debugLevel>3){
if ((c == 0) && (debugLevel > 1)) {
System.out.println(" xy3[0][0]=" + xy3[0][0] + " xy3[0][1]=" + xy3[0][1]);
System.out.println(" xy3[1][0]=" + xy3[1][0] + " xy3[1][1]=" + xy3[1][1]);
}
// make 3-rd harmonic frequency adjustment
// May be improved by quadratic maximums
for (int n = 0; n < 2; n++)
for (int i = 0; i < 2; i++) {
max = pixels[c][xy3[i][1] * fftSize + xy3[i][0]];
int d = -1;
for (int dir = 0; dir < dirs.length; dir++) {
double v = pixels[c][(xy3[i][1] + dirs[dir][1]) * fftSize + (xy3[i][0] + dirs[dir][0])];
if (max < v) {
max = v;
d = dir;
}
}
if (d >= 0) {
xy3[i][0] += dirs[d][0];
xy3[i][1] += dirs[d][1];
}
}
// if (debugLevel>2) {
if ((c == 0) && (debugLevel > 1)) {
System.out.println("*xy3[0][0]=" + xy3[0][0] + " xy3[0][1]=" + xy3[0][1]);
System.out.println("*xy3[1][0]=" + xy3[1][0] + " xy3[1][1]=" + xy3[1][1]);
}
double[][] xy = { { (xy3[0][0] - hsize) / 3.0, (xy3[0][1] - hsize) / 3.0 },
{ (xy3[1][0] - hsize) / 3.0, (xy3[1][1] - hsize) / 3.0 } };
if ((c == 0) && (debugLevel > 1)) {
System.out.println(" xy[0][0]=" + IJ.d2s(xy[0][0], 1) + " xy3[0][1]=" + IJ.d2s(xy[0][1], 1));
System.out.println(" xy[1][0]=" + IJ.d2s(xy[1][0], 1) + " xy3[1][1]=" + IJ.d2s(xy[1][1], 1));
}
// once more - correct 5-th:
int[][] xy5 = { { hsize + (int) Math.round(5 * xy[0][0]), hsize + (int) Math.round(5 * xy[0][1]) },
{ hsize + (int) Math.round(5 * xy[1][0]), hsize + (int) Math.round(5 * xy[1][1]) } };
// just once
if ((c == 0) && (debugLevel > 1)) {
System.out.println(" xy5[0][0]=" + xy5[0][0] + " xy5[0][1]=" + xy5[0][1]);
System.out.println(" xy5[1][0]=" + xy5[1][0] + " xy5[1][1]=" + xy5[1][1]);
}
for (int i = 0; i < 2; i++) {
max = pixels[c][xy5[i][1] * fftSize + xy5[i][0]];
int d = -1;
for (int dir = 0; dir < dirs.length; dir++) {
double v = pixels[c][(xy5[i][1] + dirs[dir][1]) * fftSize + (xy5[i][0] + dirs[dir][0])];
if (max < v) {
max = v;
d = dir;
}
}
if (d >= 0) {
xy5[i][0] += dirs[d][0];
xy5[i][1] += dirs[d][1];
}
}
if ((c == 0) && (debugLevel > 1)) {
System.out.println("*xy5[0][0]=" + xy5[0][0] + " xy5[0][1]=" + xy5[0][1]);
System.out.println("*xy5[1][0]=" + xy5[1][0] + " xy5[1][1]=" + xy5[1][1]);
}
for (int i = 0; i < 2; i++)
for (int j = 0; j < 2; j++)
xy[i][j] = (xy5[i][j] - hsize) / 5.0;
if ((c == 0) && (debugLevel > 1)) {
System.out.println(" xy[0][0]=" + IJ.d2s(xy[0][0], 1) + " xy3[0][1]=" + IJ.d2s(xy[0][1], 1));
System.out.println(" xy[1][0]=" + IJ.d2s(xy[1][0], 1) + " xy3[1][1]=" + IJ.d2s(xy[1][1], 1));
}
// now generate mask and then blur it
int maxMode = 7; // 1, 3-rd and 5-th harmonics, should be odd (now 7-th included)
double[][] dirNeg = { { -0.5, -0.5 }, { 0.5, -0.5 }, { -0.5, 0.5 }, { 0.5, 0.5 } }; // direction to
// negative mask
double[] mask = new double[fftSize * fftSize];
for (int i = 0; i < mask.length; i++)
mask[i] = 0.0;
for (int i = -maxMode; i <= maxMode; i += 2)
for (int j = -maxMode; j <= maxMode; j += 2) {
double xpc = -0.5 * (xy[0][0] * (i + j) + xy[1][0] * (i - j));
double ypc = -0.5 * (xy[0][1] * (i + j) + xy[1][1] * (i - j));
double xp = xpc + hsize;
double yp = ypc + hsize;
double w = xpc * xpc + ypc * ypc; // increase weight of high frequencies
w *= w; // even sharper
int ixp = (int) Math.round(xp);
int iyp = (int) Math.round(yp);
if ((ixp < 0) || (ixp >= fftSize) || (iyp < 0) || (iyp >= fftSize))
continue;
mask[iyp * fftSize + ixp] += w;
if ((c == 0) && (debugLevel > 1))
System.out.println(" xp=" + IJ.d2s(xp, 1) + " xm=" + IJ.d2s(yp, 1));
for (int d = 0; d < dirNeg.length; d++) {
double xm = xp + dirNeg[d][0] * xy[0][0] + dirNeg[d][1] * xy[1][0];
double ym = yp + dirNeg[d][0] * xy[0][1] + dirNeg[d][1] * xy[1][1];
int ixm = (int) Math.round(xm);
int iym = (int) Math.round(ym);
ixm = (ixm + fftSize) % fftSize;
iym = (iym + fftSize) % fftSize;
mask[iym * fftSize + ixm] -= w / dirNeg.length;
}
}
double sigmaScale = 0.2;
double averageLength = Math.sqrt(
(xy[0][0] * xy[0][0] + xy[0][1] * xy[0][1] + xy[1][0] * xy[1][0] + xy[1][1] * xy[1][1]) / 2);
if ((c == 0) && (debugLevel > 1))
ShowDoubleFloatArrays.showArrays(mask, fftSize, fftSize, "mask_color");
gb.blurDouble(mask, fftSize, fftSize, sigmaScale * averageLength, sigmaScale * averageLength, 0.01);
if ((c == 0) && (debugLevel > 1)) {
ShowDoubleFloatArrays.showArrays(mask, fftSize, fftSize,
"mask_color_blured" + IJ.d2s(sigmaScale * averageLength, 3));
double[] ppixels = new double[fftSize * fftSize];
for (int i = 0; i < ppixels.length; i++)
ppixels[i] = pixels[c][i] * mask[i];
ShowDoubleFloatArrays.showArrays(ppixels, fftSize, fftSize,
"masked-amplitude" + IJ.d2s(sigmaScale * averageLength, 3));
}
double SFM = 0.0, SF = 0.0, SM = 0;
for (int i = 0; i < mask.length; i++) {
// int x=(i % fftSize)-hsize;
// int y=(i / fftSize)-hsize;
SFM += pixels[c][i] * mask[i];
SF += pixels[c][i];
if (mask[i] > 0)
SM += mask[i]; // sum only positive masks? Or abs value? Does not really matter, it is just a
// scale
}
int S0 = fftSize * fftSize;
spectralContrast[c] = S0 * SFM / SF / SM;
if ((c == 0) && (debugLevel > 1))
System.out.println("SFM=" + (SFM / S0) + " SF=" + (SF / S0) + " SM=" + (SM / S0) + " averageLength="
+ averageLength);
if ((debugLevel > 1))
System.out.println("spectrumContrast[" + c + "]=" + spectralContrast[c]);
} else {
spectrumMaximums[c] = null;
spectralContrast[c] = Double.NaN;
}
// return
// 0.25*(spectralContrast[0]+spectralContrast[1]+spectralContrast[2]+spectralContrast[3]);
return 0.5 * (spectralContrast[0] + spectralContrast[3]); // green only
}
public void resetCorrelationSizesUsed() {
this.correlationSizesUsed = null;
}
public void setCorrelationSizesUsed(int size) {
int maxSize = 20;
if (this.correlationSizesUsed == null) {
this.correlationSizesUsed = new boolean[maxSize];
for (int i = 0; i < maxSize; i++)
this.correlationSizesUsed[i] = false;
}
int ln2;
for (ln2 = 0; size > (1 << ln2); ln2++)
;
if (size != (1 << ln2)) {
String msg = "Not a power of 2 :" + size;
IJ.showMessage("Error", msg);
throw new IllegalArgumentException(msg);
}
if (ln2 >= maxSize) {
String msg = "Too large array length, increase maxSize (it is now " + maxSize + ", wanted " + ln2 + ")";
IJ.showMessage("Error", msg);
throw new IllegalArgumentException(msg);
}
this.correlationSizesUsed[ln2] = true;
}
public boolean[] getCorrelationSizesUsed() {
return this.correlationSizesUsed;
}
/*
* returns array of 3 arrays: first two are 3-element wave vectors (x,y,phase),
* last - 3-rd order correction coefficients
*/
public double[][] findPatternDistorted(double[][] bayer_mono_pixels, // pixel array to process (no windowing!), two
// greens will be used
PatternDetectParameters patternDetectParameters, double min_half_period, double max_half_period,
boolean greens, // this is a pattern for combined greens (diagonal), adjust results accordingly
String title) { // title prefix to use for debug images
if (bayer_mono_pixels == null)
return null;
if (bayer_mono_pixels.length < 1)
return null;
if (bayer_mono_pixels[0] == null)
return null;
if (greens) {
if (bayer_mono_pixels.length < 4)
return null;
if (bayer_mono_pixels[3] == null)
return null;
} else {
if (bayer_mono_pixels.length > 1)
return null;
}
boolean is_mono = bayer_mono_pixels.length == 1;
int tile_size2 = bayer_mono_pixels[0].length;
int tile_size = (int) Math.sqrt(tile_size2);
int hsize = tile_size / 2;
int hsize2 = hsize * hsize;
double[] quarterHamming = initWindowFunction(hsize, patternDetectParameters.gaussWidth);
double[] patternCorr = new double[6]; // second order non-linear pattern correction (perspective+distortion)
double[] green0 = new double[hsize2];
double[] green3 = new double[hsize2];
double[][] quarter_pixels = new double[9][];
double[][][] quarter_patterns = new double[9][][];
int[] quarterIndex = { 0, // top left
tile_size / 2, // top right
tile_size * tile_size / 2, // bottom left
(tile_size + 1) * tile_size / 2, // bottom right
(tile_size + 1) * tile_size / 4, // center
tile_size / 4, // top
tile_size * tile_size / 4, // left
(tile_size + 2) * tile_size / 4, // right
(2 * tile_size + 1) * tile_size / 4 }; // bottom
int i, j, iq;
int index, qindex;
if (this.debugLevel > 2)
ShowDoubleFloatArrays.showArrays(bayer_mono_pixels, tile_size, tile_size, title + "-bayer");
for (iq = 0; iq < 9; iq++) {
index = quarterIndex[iq];
qindex = 0;
if (is_mono) {
quarter_pixels[iq] = new double[hsize * hsize];
for (i = 0; i < hsize; i++) {
for (j = 0; j < hsize; j++) { // quarter_pixels[iq][qindex++]=input_pixels[index++];
quarter_pixels[iq][qindex++] = bayer_mono_pixels[0][index++];
}
index += hsize; // jump to the next line
}
} else {
for (i = 0; i < hsize; i++) {
for (j = 0; j < hsize; j++) { // quarter_pixels[iq][qindex++]=input_pixels[index++];
green0[qindex] = bayer_mono_pixels[0][index];
green3[qindex++] = bayer_mono_pixels[3][index++];
}
// quarter_pixels[iq]=combineDiagonalGreens (
// green0,
// green3,
// hsize,
// hsize);
index += hsize; // jump to the next line
}
// Moved outside of the i-loop, that was wrong!
quarter_pixels[iq] = combineDiagonalGreens(green0, green3, hsize, hsize);
}
quarter_pixels[iq] = normalizeAndWindow(quarter_pixels[iq], quarterHamming);
if (this.debugLevel > 2)
ShowDoubleFloatArrays.showArrays(quarter_pixels[iq], hsize, hsize, title + "-new" + iq);
// findPattern - see MSP 3290:
quarter_patterns[iq] = findPattern(null, // DoubleFHT doubleFHT,
quarter_pixels[iq], hsize, patternDetectParameters, min_half_period, max_half_period, greens,
title + "Q_" + iq);
if (quarter_patterns[iq] == null)
return null;
}
if (this.debugLevel > 2) {
for (iq = 0; iq < 9; iq++) {
System.out.println("Quarter=" + iq + " W0x=" + IJ.d2s(quarter_patterns[iq][0][0], 4) + " W0y="
+ IJ.d2s(quarter_patterns[iq][0][1], 4) + " W0_phase=" + IJ.d2s(quarter_patterns[iq][0][2], 2)
+ " W1x=" + IJ.d2s(quarter_patterns[iq][1][0], 4) + " W1y="
+ IJ.d2s(quarter_patterns[iq][1][1], 4) + " W1_phase=" + IJ.d2s(quarter_patterns[iq][1][2], 2));
}
}
/*
* Filter pattern coefficients to make sure they all match between quadrants
* (match to the center one)
*/
boolean patternsMatchedInitially = matchPatterns(quarter_patterns, quarter_patterns[4]); // use center pattern
if (this.debugLevel > 2) {
System.out.println(
patternsMatchedInitially ? "All quadrant wave vectors matched initially, no correction needed"
: "Some quadrant wave vectors were adjusted to match");
}
patternCorr = calcPatternNonLinear(quarter_patterns); // divide results by ,(FFT_SIZE/2)^2 - only first 5
// patterns are used
if (this.debugLevel > 2) { /* increase LEVEL later */
System.out.println("Pre- (1000x) " + " Ax=" + IJ.d2s(1000 * patternCorr[0] / (FFT_SIZE / 2), 5) + " Bx="
+ IJ.d2s(1000 * patternCorr[1] / (FFT_SIZE / 2), 5) + " Cx="
+ IJ.d2s(1000 * patternCorr[2] / (FFT_SIZE / 2), 5) + " Ay="
+ IJ.d2s(1000 * patternCorr[3] / (FFT_SIZE / 2), 5) + " By="
+ IJ.d2s(1000 * patternCorr[4] / (FFT_SIZE / 2), 5) + " Cy="
+ IJ.d2s(1000 * patternCorr[5] / (FFT_SIZE / 2), 5) + " Dx=" + IJ.d2s(1000 * patternCorr[6], 5)
+ " Ex=" + IJ.d2s(1000 * patternCorr[7], 5) + " Dy=" + IJ.d2s(1000 * patternCorr[8], 5) + " Ey="
+ IJ.d2s(1000 * patternCorr[9], 5));
}
patternCorr = refinePatternNonLinear(quarter_patterns, // [tl,tr,bl,br, center][wv0, wv1][x,y,phase]
patternCorr, // [ax,bx,cx,ay,by,cy]
hsize); // distance to quadrats center in sensor pixels ==FFT_SIZE/2
// for (i=0;i 2) { /* increase LEVEL later */
System.out.println("Corr (1000x) " + " Ax=" + IJ.d2s(1000 * patternCorr[0], 5) + " Bx="
+ IJ.d2s(1000 * patternCorr[1], 5) + " Cx=" + IJ.d2s(1000 * patternCorr[2], 5) + " Ay="
+ IJ.d2s(1000 * patternCorr[3], 5) + " By=" + IJ.d2s(1000 * patternCorr[4], 5) + " Cy="
+ IJ.d2s(1000 * patternCorr[5], 5) + " Dx=" + IJ.d2s(1000 * patternCorr[6], 5) + " Ex="
+ IJ.d2s(1000 * patternCorr[7], 5) + " Dy=" + IJ.d2s(1000 * patternCorr[8], 5) + " Ey="
+ IJ.d2s(1000 * patternCorr[9], 5));
}
double[][] result = new double[3][];
result[0] = quarter_patterns[4][0].clone();
result[1] = quarter_patterns[4][1].clone();
result[2] = patternCorr.clone();
return result;
}
/* ======================================================================== */
public double[] correlationContrast(double[] pixels, // square pixel array
double[] widowedGreens, // array to normalize correlation result
double[][] wVectors, // wave vectors (same units as the pixels array)
double contrastSelectSigmaCenter, // Gaussian sigma to select correlation centers (in PIXELS), 2.0
double contrastSelectSigmaOther, // Gaussian sigma to select correlation off-centers centers (fraction of UV
// period), 0.1
double x0, // center coordinates
double y0, String title) {
// for now - just comparison, later - switch to
return correlationContrast(pixels, // square pixel array
wVectors, // wave vectors (same units as the pixels array)
contrastSelectSigmaCenter, // Gaussian sigma to select correlation centers (in PIXELS), 2.0
contrastSelectSigmaOther, // Gaussian sigma to select correlation off-centers centers (fraction of UV
// period), 0.1
x0, // center coordinates
y0, title, // title base for optional plots names
this.debugLevel);
}
public double correlationContrastOld(double[] pixels, // square pixel array
double[][] wVectors, // wave vectors (same units as the pixels array)
double ringWidth, // ring (around r=0.5 dist to opposite corr) width
double x0, // center coordinates
double y0, String title, // title base for optional plots names
int debugLevel) {
int size = (int) Math.sqrt(pixels.length);
double[] xy = new double[2];
double[] uv;
double r2, d;
int i, j;
/*
* opposite sign correlation points in uv are at uv=(0,-0.5),(0,0.5), (-0.5,0)
* and (0.5,0), with radius of (1/2) selecting center circle and a ring from
* 0.25 to 0.75 of the distance to opposite sign correlations
*/
double r2WingsOuter = 0.0625 * (1.0 + ringWidth) * (1.0 + ringWidth);
double r2WingsInner = 0.0625 * (1.0 - ringWidth) * (1.0 - ringWidth);
double r2Center = 0.0625 * (ringWidth) * (ringWidth);
if (debugLevel > 2)
System.out.println("rWingsOuter=" + Math.sqrt(r2WingsOuter) + " rWingsInner=" + Math.sqrt(r2WingsInner)
+ " rCenter=" + Math.sqrt(r2Center));
double valCenter = 0.0;
double valWings = 0.0;
double numCenter = 0.0;
double numWings = 0.0;
for (i = 0; i < size; i++) {
xy[1] = i - size / 2 - y0;
for (j = 0; j < size; j++) {
xy[0] = j - size / 2 - x0;
uv = matrix2x2_mul(wVectors, xy);
r2 = uv[0] * uv[0] + uv[1] * uv[1];
if (r2 <= r2WingsOuter) {
d = pixels[i * size + j];
if (r2 <= r2Center) {
valCenter += d * d;
numCenter += 1.0;
} else if (r2 > r2WingsInner) {
valWings += d * d;
numWings += 1.0;
}
}
}
}
if ((numWings == 0.0) || (numCenter == 0.0)) {
if (debugLevel > 1)
System.out.println("Not enough data for correlation contrast: numCenter=" + numCenter + " numWings="
+ numWings + " valCenter=" + IJ.d2s(valCenter, 2) + " valWings=" + IJ.d2s(valWings, 2));
return -1.0;
}
double contrast = Math.sqrt((valCenter / numCenter) / (valWings / numWings));
if (debugLevel > 2) {
System.out.println("Correlation contrast is " + contrast);
double[] maskedPixels = new double[size * size];
double[] u_value = new double[size * size];
double[] v_value = new double[size * size];
int index;
for (i = 0; i < size; i++) {
xy[1] = i - size / 2 - y0;
for (j = 0; j < size; j++) {
xy[0] = j - size / 2 - x0;
uv = matrix2x2_mul(wVectors, xy);
r2 = uv[0] * uv[0] + uv[1] * uv[1];
index = i * size + j;
u_value[index] = uv[0];
v_value[index] = uv[1];
/*
* r=Math.sqrt(r2); r-=Math.floor(r); floatPixels[index]=(float) r;
*/
if (((r2 <= r2WingsOuter) && (r2 > r2WingsInner)) || (r2 <= r2Center)) {
maskedPixels[index] = pixels[index];
} else {
maskedPixels[index] = 0.0;
}
}
}
double[][] dbgPixels = { pixels, maskedPixels, u_value, v_value };
String[] titles = { "all", "masked", "u", "v" };
ShowDoubleFloatArrays.showArrays(dbgPixels, size, size, true, title + "_CORR_MASK", titles);
}
return contrast;
}
public double[] correlationContrast(double[] pixels, // square pixel array
double[][] wVectors, // wave vectors (same units as the pixels array)
double sigma_center, // GGaussian sigma to select correlation centers (in PIXELS), 1.5
double sigma_other, // Gaussian sigma to select correlation off-centers centers (fraction of UV
// period), 0.1
double x0, // center coordinates
double y0, String title, // title base for optional plots names
int debugLevel) {
double avg_rad = 2.0 / Math.sqrt(wVectors[0][0] * wVectors[0][0] + wVectors[0][1] * wVectors[0][1]
+ wVectors[1][0] * wVectors[1][0] + wVectors[1][1] * wVectors[1][1]);
double max_center_sigma = 0.05 * avg_rad; // approximate - make configurable or just relative rather than
// absolute?
if (sigma_center > max_center_sigma) {
sigma_center = max_center_sigma;
}
double[] badContrasts = { -1.0, -1.0 };
double sigma32_center = 9 * sigma_center * sigma_center; // in pixels
double k_center = -0.5 / (sigma_center * sigma_center); // in pixels
double sigma32_other = 9 * sigma_other * sigma_other; // in periods
double k_other = -0.5 / (sigma_other * sigma_other); // in periods
double[][] sampleCentersXY = { { 0.0, 0.0 }, { 0.25, 0.25 }, { 0.25, -0.25 }, { -0.25, 0.25 },
{ -0.25, -0.25 } };
// System.out.println("avg_rad="+avg_rad);
int[] sampleTypes = { 0, 1, 1, 1, 1 };
int size = (int) Math.sqrt(pixels.length);
double[] xy = new double[2];
double[] uv;
double r2;
int i, j;
// TODO: limit sigma_center to fit between others;
double[] dbgMask = new double[size * size];
Arrays.fill(dbgMask,0.0);
double[] s = { 0.0, 0.0 };
double[] w = { 0.0, 0.0 };
double max_center = 0.0;
// double [] dbg_weights = new double [size*size];
for (i = 0; i < size; i++) {
xy[1] = i - size / 2 - y0;
for (j = 0; j < size; j++) {
int index = i * size + j;
xy[0] = j - size / 2 - x0;
uv = matrix2x2_mul(wVectors, xy);
for (int np = 0; np < sampleCentersXY.length; np++) {
if (sampleTypes[np] == 0) { // center spot, size in pixels
r2 = xy[0] * xy[0] + xy[1] * xy[1];
if (r2 < sigma32_center) {
double m = Math.exp(k_center * r2);
dbgMask[index] += m;
w[sampleTypes[np]] += m;
double d = m * pixels[index];
max_center = Math.max(d, max_center);
// if (sampleTypes[np] > 0)
// d *= pixels[index]; // squared
s[sampleTypes[np]] += d;
}
} else { // between correlation spots, size relative to the periods
double dx = uv[0] - sampleCentersXY[np][0];
double dy = uv[1] - sampleCentersXY[np][1];
r2 = dx * dx + dy * dy;
if (r2 < sigma32_other) {
double m = Math.exp(k_other * r2);
dbgMask[index] += m;
w[sampleTypes[np]] += m;
double d = m * pixels[index];
// if (sampleTypes[np] > 0)
d *= pixels[index]; // squared
s[sampleTypes[np]] += d;
}
}
}
}
}
if ((w[0] == 0.0) || (w[1] == 0.0)) {
if (debugLevel > 1)
System.out.println(
"Not enough data for correlation contrast: center - w[0]=" + w[0] + " opposite - w[1]=" + w[1]);
return badContrasts;
}
double[][] dbg_corr_mask = { pixels, dbgMask };
// double aCenter = s[0] / w[0];
double aCenter = max_center;
double aQuiet = Math.sqrt(s[1] / w[1]);
double rContrast = aCenter / aQuiet;
// double aContrast = aCenter / size / size;
double aContrast = aCenter; // / size / size;
double[] contrasts = { rContrast, aContrast };
if (debugLevel > 2) {
System.out.println("correlationContrast() rContrast=" + rContrast + " aContrast=" + aContrast + " aCenter="
+ aCenter + " aQuiet=" + aQuiet + " w[0]=" + w[0] + " w[1]=" + w[1] + " s[0]=" + s[0] + " s[1]="
+ s[1]);
}
if (debugLevel > 2) {
System.out.println("Correlation contrast is: relative=" + rContrast + " absolute=" + aContrast);
double[][] dbgPixels = { pixels, dbgMask };
String[] titles = { "all", "mask" };
ShowDoubleFloatArrays.showArrays(dbgPixels, size, size, true, title + "_MASK", titles);
}
return contrasts;
}
public double correlationContrastOld2(double[] pixels, // square pixel array
double[] widowedGreens, // array to normailze correlation result
double[][] wVectors, // wave vectors (same units as the pixels array)
double sigma, double sigmaNorm, // to measure variations for normalization of the contrast
double x0, // center coordinates
double y0, String title, // title base for optional plots names
int debugLevel) {
// TODO: make configurable parameters
// double sigma=0.1;
// double sigmaNorm=0.5; // to measure variations for normalization of the
// contrast
double sigma32 = 9 * sigma * sigma;
double k = -0.5 / (sigma * sigma);
double sigmaNorm32 = 9 * sigmaNorm * sigmaNorm;
double kNorm = -0.5 / (sigmaNorm * sigmaNorm);
double[][] sampleCentersXY = { { 0.0, 0.0 }, { 0.0, 0.5 }, { 0.5, 0.0 }, { 0.0, -0.5 }, { -0.5, 0.0 } };
int[] sampleTypes = { 0, 1, 1, 1, 1 };
int size = (int) Math.sqrt(pixels.length);
double[] xy = new double[2];
double[] uv;
double r2;
int i, j;
/*
* opposite sign correlation points in uv are at uv=(0,-0.5),(0,0.5), (-0.5,0)
* and (0.5,0), with radius of (1/2) selecting center circle and a ring from
* 0.25 to 0.75 of the distance to opposite sign correlations
*/
double[] dbgMask = new double[size * size];
for (int n = 0; n < dbgMask.length; n++)
dbgMask[n] = 0.0;
double[] s = { 0.0, 0.0 };
double[] w = { 0.0, 0.0 };
double S0 = 0.0, S1 = 0.0, S2 = 0.0;
double SG1 = 0.0, SG2 = 0.0;
// Find measured pixels variations in the window
for (i = 0; i < size; i++) {
xy[1] = i - size / 2 - y0;
for (j = 0; j < size; j++) {
int index = i * size + j;
xy[0] = j - size / 2 - x0;
uv = matrix2x2_mul(wVectors, xy);
for (int np = 0; np < sampleCentersXY.length; np++) {
double dx = uv[0] - sampleCentersXY[np][0];
double dy = uv[1] - sampleCentersXY[np][1];
r2 = dx * dx + dy * dy;
if (r2 < sigma32) {
double m = Math.exp(k * r2);
dbgMask[index] += m;
w[sampleTypes[np]] += m;
s[sampleTypes[np]] += m * pixels[index];
}
}
r2 = uv[0] * uv[0] + uv[1] * uv[1];
if (r2 < sigmaNorm32) {
double m = Math.exp(kNorm * r2);
S0 += m;
S1 += m * pixels[index];
S2 += m * pixels[index] * pixels[index];
SG1 = m * widowedGreens[index];
SG2 = m * widowedGreens[index] * widowedGreens[index];
}
}
}
if ((w[0] == 0.0) || (w[1] == 0.0)) {
if (debugLevel > 1)
System.out.println(
"Not enough data for correlation contrast: center - w[0]=" + w[0] + " opposite - w[1]=" + w[1]);
return -1.0;
}
double ref = Math.sqrt(S2 * S0 - S1 * S1) / S0;
double refG = Math.sqrt(SG2 * S0 - SG1 * SG1) / S0;
double contrast = ((s[0] / w[0]) - (s[1] / w[1])) / ref;
double contrastG = ((s[0] / w[0]) - (s[1] / w[1])) / refG; /// size;
if (debugLevel > 2) {
System.out.println("correlationContrast() corr_diff=" + (((s[0] / w[0]) - (s[1] / w[1]))) + " contrast="
+ contrast + " w[0]=" + w[0] + " w[1]=" + w[1] + " s[0]=" + s[0] + " s[1]=" + s[1]);
System.out.println("correlationContrast() S0=" + S0 + " S1=" + S1 + " S2=" + S2 + " ref=" + ref);
System.out.println("correlationContrast() contrastG=" + contrastG + " S0=" + S0 + " SG1=" + SG1 + " SG2="
+ SG2 + " refG=" + refG);
}
// if (contrast>3.0){
// System.out.println("correlationContrast() contrast="+contrast+" w[0]="+w[0]+"
// w[1]="+w[1]+" s[0]="+s[0]+" s[1]="+s[1]+" S0="+S0+" S1="+S1+" S2="+S2+"
// ref="+ref);
// }
// double contrast=Math.sqrt((s[0]/w[0]) /(s[1]/w[1]));
// double contrast=((s[0]/w[0]) -(s[1]/w[1]))/(size*size);
if (debugLevel > 2) {
System.out.println("Correlation contrast is " + contrast);
double[][] dbgPixels = { pixels, dbgMask };
String[] titles = { "all", "mask" };
ShowDoubleFloatArrays.showArrays(dbgPixels, size, size, true, title + "_CORR_MASK", titles);
}
return contrast;
}
/* ======================================================================== */
public double[] correlateWithModel(double[] imagePixels, // measured pixel array
double[] modelPixels, // simulated (model) pixel array)
double sigma, // Sigma for high pass filtering TODO: implement!
String title) { // title base for optional plots names
if (imagePixels.length != modelPixels.length) {
IJ.showMessage("Error", "Arrays have different sizes - imagePixels.length=" + imagePixels.length
+ ", modelPixels.length=" + modelPixels.length);
return null;
}
int size = (int) Math.sqrt(imagePixels.length);
ImageProcessor ip, ip_model;
FHT fht, fht_model;
double[][][] fft_complex, fft_model;
int i, j;
double a;
float[] floatImagePixels = new float[size * size];
/* convert to float for image processor; */
for (i = 0; i < (size * size); i++)
floatImagePixels[i] = (float) imagePixels[i];
ip = new FloatProcessor(size, size);
ip.setPixels(floatImagePixels);
fht = new FHT(ip);
// Swapping quadrants, so the center will be 0,0
fht.swapQuadrants();
// get to frequency domain
fht.transform();
floatImagePixels = (float[]) fht.getPixels();
if ((this.debugLevel > 5) && (title != "")) {
ImageProcessor ip_fht = new FloatProcessor(size, size);
ip_fht.setPixels(floatImagePixels);
ip_fht.resetMinAndMax();
ImagePlus imp_fht = new ImagePlus(title + "_FHT_image", ip_fht);
imp_fht.show();
}
// Convert from FHT to complex FFT
fft_complex = FHT2FFTHalf(fht, size);
float[] floatModelPixels = new float[size * size];
// convert to float for image processor;
for (i = 0; i < (size * size); i++)
floatModelPixels[i] = (float) modelPixels[i];
ip_model = new FloatProcessor(size, size);
ip_model.setPixels(floatModelPixels);
fht_model = new FHT(ip_model);
// Swapping quadrants, so the center will be 0,0
fht_model.swapQuadrants();
// get to frequency domain
fht_model.transform();
floatModelPixels = (float[]) fht_model.getPixels();
if ((this.debugLevel > 5) && (title != "")) {
ImageProcessor ip_fht_model = new FloatProcessor(size, size);
ip_fht_model.setPixels(floatModelPixels);
ip_fht_model.resetMinAndMax();
ImagePlus imp_fht_model = new ImagePlus(title + "_FHT_model", ip_fht_model);
imp_fht_model.show();
}
// Convert from FHT to complex FFT
fft_model = FHT2FFTHalf(fht_model, size);
// multiply fft_complex by fft_nominator
for (i = 0; i < fft_complex.length; i++)
for (j = 0; j < fft_complex[0].length; j++) {
a = fft_complex[i][j][0] * fft_model[i][j][0] + fft_complex[i][j][1] * fft_model[i][j][1]; // already
// changed
// Im() sign
fft_complex[i][j][1] = -fft_complex[i][j][0] * fft_model[i][j][1]
+ fft_complex[i][j][1] * fft_model[i][j][0]; // already changed Im() sign
fft_complex[i][j][0] = a;
}
/* Add sigma high=pass filtering here */
// Convert fft array back to fht array and
// set fht_target pixels with new values
fht.setPixels(floatFFTHalf2FHT(fft_complex, size));
/// optionally show the result
if ((this.debugLevel > 5) && (title != "")) {
ImageProcessor ip_fht2 = new FloatProcessor(size, size);
ip_fht2.setPixels(floatFFTHalf2FHT(fft_complex, size));
ip_fht2.resetMinAndMax();
ImagePlus imp_fht2 = new ImagePlus(title + "-corr-sigma" + sigma, ip_fht2);
imp_fht2.show();
}
/// transform to space
fht.inverseTransform();
fht.swapQuadrants();
fht.resetMinAndMax();
// ImagePlus imp= new ImagePlus(title, ip_fht);
if ((this.debugLevel > 2) && (title != "")) {
ImagePlus imp_corr = new ImagePlus(title + "_Correlated_filt-" + sigma, fht);
imp_corr.show();
}
// return direct_target;
floatImagePixels = (float[]) fht.getPixels();
double[] pixels = new double[floatImagePixels.length];
for (i = 0; i < floatImagePixels.length; i++)
pixels[i] = floatImagePixels[i];
return pixels;
}
/**
* Refining non-linear mesh matching by comparing phases in the centers of 4
* quadrants and the very center. Can only compensate to a fraction of mesh
* period (TBD - total range, probably +/-half period), so non-linear
* coefficients should be already known to that precision 9 measurements are
* used here - top-left, top-right,bottom-left, bottom-right, center, top, left,
* right,bottom
*
*/
private double[] refinePatternNonLinear(double[][][] qp, // [tl,tr,bl,br, center][wv0, wv1][x,y,phase]
double[] nonlin, // [ax,bx,cx,ay,by,cy]
int size) { // distance to quadrants center in sensor pixels ==FFT_SIZE/2
int iq, i, j;
double[][] xy = new double[qp.length][2];
double[] uv = new double[2];
double[] duv = new double[2];
// double [][][] wl=new double [5][2][2]; // Wave length vectors - same
// direction as wavevectors, length=distance between wavefronts
double[][][] wp = new double[9][2][3]; // pattern vectors (with phase)
double x1, y1;
double xq = 0;
double yq = 0;
// probably only wl[4] is needed
for (iq = 0; iq < 9; iq++)
for (i = 0; i < 2; i++)
for (j = 0; j < 2; j++)
wp[iq] = waveVectorsToPatternVectors(qp[iq][0], qp[iq][1]);
if (this.debugLevel > 2) { /* increase LEVEL later */
for (iq = 0; iq < 5; iq++) {
System.out.println(" wp[" + iq + "][0][0]=" + IJ.d2s(wp[iq][0][0], 4) + " wp[" + iq + "][0][1]="
+ IJ.d2s(wp[iq][0][1], 4) + " wp[" + iq + "][0][2]=" + IJ.d2s(wp[iq][0][2], 4) + "("
+ IJ.d2s(wp[iq][0][2] / 2 / Math.PI, 4) + ")" + " wp[" + iq + "][1][0]="
+ IJ.d2s(wp[iq][1][0], 4) + " wp[" + iq + "][1][1]=" + IJ.d2s(wp[iq][1][1], 4) + " wp[" + iq
+ "][1][2]=" + IJ.d2s(wp[iq][1][2], 4) + "(" + IJ.d2s(wp[iq][1][2] / 2 / Math.PI, 4) + ")");
}
}
for (iq = 0; iq < 9; iq++) {
if (iq == 4)
continue; // nothing to calculate in the center
switch (iq) {
case 0:
xq = -1.0;
yq = -1.0;
break;
case 1:
xq = 1.0;
yq = -1.0;
break;
case 2:
xq = -1.0;
yq = 1.0;
break;
case 3:
xq = 1.0;
yq = 1.0;
break;
case 4:
xq = 0.0;
yq = 0.0;
break;
case 5:
xq = 0.0;
yq = -1.0;
break;
case 6:
xq = -1.0;
yq = 0.0;
break;
case 7:
xq = 1.0;
yq = 0.0;
break;
case 8:
xq = 0.0;
yq = 1.0;
break;
}
x1 = size * (xq + nonlin[0] * xq * xq + nonlin[1] * yq * yq + 2 * nonlin[2] * xq * yq + nonlin[6] * xq
+ nonlin[7] * yq); // in pixels
y1 = size * (yq + nonlin[3] * xq * xq + nonlin[4] * yq * yq + 2 * nonlin[5] * xq * yq + nonlin[8] * xq
+ nonlin[9] * yq); // in pixels
/* convert x1,y1 into wp vector coordiantes */
uv[0] = (wp[4][1][1] * x1 - wp[4][1][0] * y1) / (wp[4][0][0] * wp[4][1][1] - wp[4][0][1] * wp[4][1][0]); // wl
// in
// center
// vectors,
// not
// local
// !
uv[1] = (wp[4][0][0] * y1 - wp[4][0][1] * x1) / (wp[4][0][0] * wp[4][1][1] - wp[4][0][1] * wp[4][1][0]);
/* Actually phases seem to be the same? */
duv[0] = uv[0] - Math.round(uv[0]) - (wp[iq][0][2] - wp[4][0][2]) / (Math.PI * 2);
duv[1] = uv[1] - Math.round(uv[1]) - (wp[iq][1][2] - wp[4][1][2]) / (Math.PI * 2);
if (this.debugLevel > 2) { /* increase LEVEL later */
System.out.println("iq= " + iq + " x1=" + IJ.d2s(x1, 4) + " y1=" + IJ.d2s(y1, 4) + " uv[0]"
+ IJ.d2s(uv[0], 4) + " uv[1]" + IJ.d2s(uv[1], 4) + " duv[0]" + IJ.d2s(duv[0], 4) + " duv[1]"
+ IJ.d2s(duv[1], 4));
}
/* re-normalize phases to be +/- 0.5 (+/-PI) range */
duv[0] = duv[0] - Math.round(duv[0]);
duv[1] = duv[1] - Math.round(duv[1]);
if (this.debugLevel > 2) { /* increase LEVEL later */
System.out.println("iq= " + iq + " ------- " + " duv[0]" + IJ.d2s(duv[0], 4)
+ /* ======================================================================== */
" duv[1]" + IJ.d2s(duv[1], 4));
}
/* Fix half period vertical/half period horizontal shift - is that needed? */
if (Math.abs(duv[0]) > 0.25) {
duv[0] += 0.5;
duv[1] += 0.5;
duv[0] = duv[0] - Math.round(duv[0]);
duv[1] = duv[1] - Math.round(duv[1]);
if (this.debugLevel > 2) {
System.out.println("Correct phase shift >0.25 in quadrant " + iq + ", now" + " duv[0]"
+ IJ.d2s(duv[0], 4) + " duv[1]" + IJ.d2s(duv[1], 4));
}
}
/* Verify here that phase adjustment is within range, fail otherwise */
if ((Math.abs(duv[0]) > 0.5) || (Math.abs(duv[1]) > 0.5)) {
if (this.debugLevel > 0) {
System.out.println("Error: in quadrant " + iq
+ " - attempted to adjust phase too much (>+/- pi/2), keeping initial parematers");
}
return nonlin;
}
/* convert duv to x,y */
xy[iq][0] = x1 - wp[4][0][0] * duv[0] - wp[4][1][0] * duv[1];
xy[iq][1] = y1 - wp[4][0][1] * duv[0] - wp[4][1][1] * duv[1];
if (this.debugLevel > 2) { /* increase LEVEL later */
System.out.println(
" xy[" + iq + "][0]=" + IJ.d2s(xy[iq][0], 4) + " xy[" + iq + "][1]=" + IJ.d2s(xy[iq][1], 4));
}
/*
* convert xy to non-linear differences, remove pixels dimensions - quadrats +/-
* 1.0
*/
xy[iq][0] = (xy[iq][0] - size * xq) / size;
xy[iq][1] = (xy[iq][1] - size * yq) / size;
if (this.debugLevel > 2) { /* increase LEVEL later */
System.out.println("Quadrant " + iq + ": Original non-linear difference was" + " x="
+ IJ.d2s(nonlin[0] * xq * xq + nonlin[1] * yq * yq + nonlin[2] * xq * yq, 4) + " y="
+ IJ.d2s(nonlin[3] * xq * xq + nonlin[4] * yq * yq + nonlin[5] * xq * yq, 4));
System.out.println("Quadrant " + iq + ": Refined non-linear difference is" + " x="
+ IJ.d2s(xy[iq][0], 4) + " y=" + IJ.d2s(xy[iq][1], 4));
}
}
/*
* Do the refinement itself - recalculate coefficients minimizing errors in 5
* points
*/
/**
*
* x1=size*(xq + nonlin[0]*xq*xq+ nonlin[1]*yq*yq+ 2* nonlin[2]*xq*yq
* +nonlin[6]*xq+ nonlin[7]*yq); // in pixels y1=size*(yq + nonlin[3]*xq*xq+
* nonlin[4]*yq*yq+ 2* nonlin[5]*xq*yq +nonlin[8]*xq+ nonlin[9]*yq); // in
* pixels dx=AX*x^2 +BX*y^2 +2*CX*x*y + DX*x +EX *y dy=AY*x^2 +BY*y^2 +2*CY*x*y
* + DY*x +EY *y
*
* dx0= AX +BX +2*CX -DX -EX dy0= AY +BY +2*CY -DY -EY
*
* dx1= AX +BX -2*CX +DX -EX dy1= AY +BY -2*CY +DY -EY
*
* dx2= AX +BX -2*CX -DX +EX dy2= AY +BY -2*CY -DY +EY
*
* dx3= AX +BX +2*CX +DX +EX dy3= AY +BY +2*CY +DY +EY
*
* dx5= +BX -EX dy5= +BY -EY
*
* dx6= AX -DX dy6= AY -DY
*
* dx7= AX +DX dy7= AY +DY
*
* dx8= +BX +EX dy8= +BY +EY
*
* //--------- -(1) dx0= AX +BX +2*CX -DX -EX -(2) dx1= AX +BX -2*CX +DX -EX
* +(3) dx2= AX +BX -2*CX -DX +EX +(4) dx3= AX +BX +2*CX +DX +EX -(5) dx5= +BX
* -EX (6) dx6= AX -DX (7) dx7= AX +DX +(8) dx8= +BX +EX
* -(1)+(2)-(3)+(4)-(6)+(7) DX=()-dx0+dx1-dx2+dx3-dx6+dx7)/6
* -(1)-(2)+(3)+(4)-(5)+(8) EX=()-dx0-dx1+dx2+dx3-dx5+dx8)/6
* -(1)-(2)+(3)+(4)-(5)+(8) EY=()-dy0-dy1+dy2+dy3-dy5+dy8)/6
* -(1)+(2)-(3)+(4)-(6)+(7) DY=()-dy0+dy1-dy2+dy3-dy6+dy7)/6
*
* AX+BX+2*CX = p1x= (dx0+dx3)/2 AY+BY+2*CY = p1y= (dy0+dy3)/2 AX+BX-2*CX = p2x=
* (dx1+dx2)/2 AY+BY-2*CY = p2y= (dy1+dy2)/2 BX = p3x= (dx5+dx8)/2 BY = p3y=
* (dy5+dy8)/2 AX = p4x= (dx6+dx7)/2 AY = p4y= (dy6+dy7)/2 //minimizing sum of
* squares of errors: CX= (p1x-p2x)/4 CY= (p1y-p2y)/4 AX=
* (3*p4x-2*p3x+p1x+p2x)/5 AY= (3*p4y-2*p3y+p1y+p2y)/5 BX=
* (3*p3x-2*p4x+p1x+p2x)/5 BY= (3*p3y-2*p4y+p1y+p2y)/5
*/
double p1x = (xy[0][0] + xy[3][0]) / 2;
double p1y = (xy[0][1] + xy[3][1]) / 2;
double p2x = (xy[1][0] + xy[2][0]) / 2;
double p2y = (xy[1][1] + xy[2][1]) / 2;
double p3x = (xy[5][0] + xy[8][0]) / 2;
double p3y = (xy[5][1] + xy[8][1]) / 2;
double p4x = (xy[6][0] + xy[7][0]) / 2;
double p4y = (xy[6][1] + xy[7][1]) / 2;
double[] rslt = new double[10];
rslt[2] = (p1x - p2x) / 4; // CX
rslt[5] = (p1y - p2y) / 4; // CY
rslt[0] = (3 * p4x - 2 * p3x + p1x + p2x) / 5; // AX
rslt[3] = (3 * p4y - 2 * p3y + p1y + p2y) / 5; // AY
rslt[1] = (3 * p3x - 2 * p4x + p1x + p2x) / 5; // BX
rslt[4] = (3 * p3y - 2 * p4y + p1y + p2y) / 5; // BY
/*
* -(1)+(2)-(3)+(4)-(6)+(7) DX=(-dx0+dx1-dx2+dx3-dx6+dx7)/6
* -(1)-(2)+(3)+(4)-(5)+(8) EX=(-dx0-dx1+dx2+dx3-dx5+dx8)/6
* -(1)-(2)+(3)+(4)-(5)+(8) EY=(-dy0-dy1+dy2+dy3-dy5+dy8)/6
* -(1)+(2)-(3)+(4)-(6)+(7) DY=(-dy0+dy1-dy2+dy3-dy6+dy7)/6
*/
rslt[6] = (-xy[0][0] + xy[1][0] - xy[2][0] + xy[3][0] - xy[6][0] + xy[7][0]) / 6; // DX=(-dx0+dx1-dx2+dx3-dx6+dx7)/6
rslt[7] = (-xy[0][0] - xy[1][0] + xy[2][0] + xy[3][0] - xy[5][0] + xy[8][0]) / 6; // EX=(-dx0-dx1+dx2+dx3-dx5+dx8)/6
rslt[8] = (-xy[0][1] + xy[1][1] - xy[2][1] + xy[3][1] - xy[6][1] + xy[7][1]) / 6; // EY=(-dy0-dy1+dy2+dy3-dy5+dy8)/6
rslt[9] = (-xy[0][1] - xy[1][1] + xy[2][1] + xy[3][1] - xy[5][1] + xy[8][1]) / 6; // DY=(-dy0+dy1-dy2+dy3-dy6+dy7)/6
return rslt; // temporary
}
/* ======================================================================== */
public double[][] findPatternUsedNow(double[] input_pixels, // pixel array to process
int size, // FFT size
PatternDetectParameters patternDetectParameters, boolean greens, // this is a pattern for combined greens
// (diagonal), adjust results
// accordingly
String title) { // title prefix to use for debug images
double[] pixels = input_pixels.clone();
double[][] result = new double[2][3];
// System.out.println("pixels.length="+pixels.length); //4096
int local_debug_level = this.debugLevel; // to change it in debugger
ImageProcessor ip, ip1;
FHT fht, fht1;
double[][][] fft_complex, fft_corr;
double[][] fft_gamma;
int i, j;
double DCLevel = 0.0;
double a;
float[] floatPixels = new float[pixels.length];
for (i = 0; i < pixels.length; i++)
DCLevel += pixels[i];
DCLevel /= (size * size);
for (i = 0; i < pixels.length; i++)
pixels[i] -= DCLevel;
// convert to float for image processor;
for (i = 0; i < pixels.length; i++)
floatPixels[i] = (float) pixels[i];
ip = new FloatProcessor(size, size);
ip.setPixels(floatPixels);
if (local_debug_level > 8) {
ip.resetMinAndMax();
ImagePlus imp_direct = new ImagePlus(title + "_Direct_" + patternDetectParameters.corrGamma, ip);
imp_direct.show();
}
fht = new FHT(ip);
// Swapping quadrants, so the center will be 0,0
fht.swapQuadrants();
// get to frequency domain
fht.transform();
if (local_debug_level > 5)
ShowDoubleFloatArrays.showArrays((float[]) fht.getPixels(), size, size, title + "_FHT");
// Convert from FHT to complex FFT
fft_complex = FHT2FFTHalf(fht, size);
// will need fft_complex again later for later phase pattern measurements,
// calculate fft_gamma for correlation (pattern 2 frequencies measurement)
fft_gamma = new double[size][size];
floatPixels = new float[pixels.length];
DCLevel = 0.0;
for (i = 0; i < fft_complex.length; i++)
for (j = 0; j < fft_complex[0].length; j++) {
fft_gamma[i][j] = Math.pow(
fft_complex[i][j][0] * fft_complex[i][j][0] + fft_complex[i][j][1] * fft_complex[i][j][1],
patternDetectParameters.corrGamma);
DCLevel += fft_gamma[i][j];
floatPixels[i * size + j] = (float) fft_gamma[i][j];
}
DCLevel /= (fft_complex.length * fft_complex[0].length);
for (i = 0; i < fft_complex.length; i++)
for (j = 0; j < fft_complex[0].length; j++) {
floatPixels[i * size + j] -= DCLevel;
if ((i > 0) && (i < (size / 2))) {
floatPixels[(size - i) * size + ((size - j) % size)] = floatPixels[i * size + j];
}
}
/*
* TODO: maybe it is better to find the pattern frequencies just here, without
* converting back. After rejecting low frequencies, there seem to be just 2
* nice maximums - easy to extract
*/
// now perform direct FFT of gamma(power spectrum)
ip1 = new FloatProcessor(size, size);
ip1.setPixels(floatPixels);
if (local_debug_level > 7) {
ip1.resetMinAndMax();
ImagePlus imp1 = new ImagePlus(title + "_gamma(ps)_" + patternDetectParameters.corrGamma, ip1);
imp1.show();
}
fht1 = new FHT(ip1);
// Swapping quadrants, so the center will be 0,0
fht1.swapQuadrants();
fht1.transform();
fft_corr = FHT2FFTHalf(fht1, size);
double[] highPassFilter = new double[fft_complex[0].length];
double expK = (patternDetectParameters.corrSigma > 0)
? (1.0 / (2 * patternDetectParameters.corrSigma * patternDetectParameters.corrSigma))
: 0.0;
for (j = 0; j <= fft_complex[0].length / 2; j++) {
highPassFilter[j] = (expK > 0.0) ? (1.0 - Math.exp(-(expK * j * j))) : 1.0;
if (j > 0)
highPassFilter[highPassFilter.length - j] = highPassFilter[j];
}
for (i = 0; i < fft_complex.length; i++)
for (j = 0; j < fft_complex[0].length; j++) {
fft_corr[i][j][0] = highPassFilter[i] * highPassFilter[j]
* (fft_corr[i][j][0] * fft_corr[i][j][0] + fft_corr[i][j][1] * fft_corr[i][j][1]);
fft_corr[i][j][1] = 0.0;
}
// Convert fft array back to fht array and
// set fht_target pixels with new values
fht1.setPixels(floatFFTHalf2FHT(fft_corr, size)); /* FIXME: - done, there is no difference as Im()==0 */
/// optionally show the result
if (local_debug_level > 7) {
ImageProcessor ip_fht2 = new FloatProcessor(size, size);
ip_fht2.setPixels(floatFFTHalf2FHT(fft_corr, size));
ip_fht2.resetMinAndMax();
ImagePlus imp_fht2 = new ImagePlus(title + "_fht_corr_" + patternDetectParameters.corrGamma, ip_fht2);
imp_fht2.show();
}
/// transform to space
fht1.inverseTransform();
floatPixels = (float[]) fht1.getPixels();
a = 1 / floatPixels[0];
for (i = 0; i < floatPixels.length; i++) {
floatPixels[i] *= a;
}
fht1.setPixels(floatPixels);
// System.out.println("2:y="+y+" x="+x+" base_b="+base_b+" base="+base);
fht1.swapQuadrants();
if (local_debug_level > 2) {
fht1.resetMinAndMax();
ImagePlus imp_corr = new ImagePlus(title + "_corr_" + patternDetectParameters.corrGamma, fht1);
imp_corr.show();
}
// return direct_target;
floatPixels = (float[]) fht1.getPixels();
for (i = 0; i < floatPixels.length; i++)
pixels[i] = floatPixels[i];
// show pixels here?
int[][] max2OnSpectrum = findFirst2MaxOnSpectrum(fft_complex, // complex, top half, starting from 0,0
1, // skip +- from (0,0) and previous max - add parameter to dialog?
0.5); // 0.5 - 30deg. orthogonality of 2 vectors - 1.0 - perpendicular, 0.0 - parallel
// - add parameter to dialog?
/** TODO: get out on failure */
if (max2OnSpectrum == null) {
if (local_debug_level > 2) {
System.out.println("findPattern() 1: Failed to find a pattern");
if (local_debug_level > 2) {
ShowDoubleFloatArrays.showArrays(input_pixels, "failed-findPattern-1-");
}
}
return null;
}
/* Trying to filter out unreasonable maximums (if there is no pattern at all) */
double maxFrequency = 0.25 * fft_complex.length;
if ((Math.abs(max2OnSpectrum[0][0]) > maxFrequency) || (Math.abs(max2OnSpectrum[0][1]) > maxFrequency)
|| (Math.abs(max2OnSpectrum[1][0]) > maxFrequency) || (Math.abs(max2OnSpectrum[1][1]) > maxFrequency)) {
if (local_debug_level > 2) {
System.out.println("Failed to detect pattern, as frequecy is above limit=" + IJ.d2s(maxFrequency, 2));
System.out.println("Maximum 1 on spectrum: x=" + IJ.d2s(max2OnSpectrum[0][0], 4) + " y="
+ IJ.d2s(max2OnSpectrum[0][1], 4));
System.out.println("Maximum 2 on spectrum: x=" + IJ.d2s(max2OnSpectrum[1][0], 4) + " y="
+ IJ.d2s(max2OnSpectrum[1][1], 4));
}
return null;
}
if (local_debug_level > 6) {
System.out.println("Maximum 1 on spectrum: x=" + IJ.d2s(max2OnSpectrum[0][0], 4) + " y="
+ IJ.d2s(max2OnSpectrum[0][1], 4));
System.out.println("Maximum 2 on spectrum: x=" + IJ.d2s(max2OnSpectrum[1][0], 4) + " y="
+ IJ.d2s(max2OnSpectrum[1][1], 4));
}
int[][] startPoints = {
{ max2OnSpectrum[0][0] + max2OnSpectrum[1][0], max2OnSpectrum[0][1] + max2OnSpectrum[1][1] },
{ max2OnSpectrum[0][0] - max2OnSpectrum[1][0], max2OnSpectrum[0][1] - max2OnSpectrum[1][1] } };
if (startPoints[1][1] < 0) { /* startPoints[1][1] > 0 anyway */
startPoints[1][0] = -startPoints[1][0];
startPoints[1][1] = -startPoints[1][1];
}
if (local_debug_level > 2) {
System.out.println("Predicted correlation maximum 1 from spectrum: x=" + IJ.d2s(startPoints[0][0], 4)
+ " y=" + IJ.d2s(startPoints[0][1], 4));
System.out.println("Predicted correlation maximum 2 from spectrum: x=" + IJ.d2s(startPoints[1][0], 4)
+ " y=" + IJ.d2s(startPoints[1][1], 4));
}
// returns frequencies (cycles/per oversampled pixel) that assume 4:1 oversampling (
double[][] max2 = findFirst2MaxOnCorrelation(pixels, startPoints, patternDetectParameters);
/** TODO: get out on failure */
if (max2 == null) {
if (local_debug_level > 2) {
System.out.println("findPattern() 2: Failed to find a pattern");
if (local_debug_level > 2) {
ShowDoubleFloatArrays.showArrays(input_pixels, "failed-findPattern-2-");
}
}
return null;
}
/* these are combined greens, convert vectors to original pixel space) */
if (greens) {
double[][] rotMatrix = { { 1.0, -1.0 }, { 1.0, 1.0 } };
double[][] max2orig = matrix2x2_mul(max2, rotMatrix);
for (i = 0; i < 2; i++)
for (j = 0; j < 2; j++)
result[i][j] = max2orig[i][j]; // result is [2][3], max2orig is [2][2]
if (local_debug_level > 2) {
System.out.println("Corrected to original pixels[0] x=" + IJ.d2s(result[0][0], 4) + " y="
+ IJ.d2s(result[0][1], 4));
System.out.println("Corrected to original pixels[1] x=" + IJ.d2s(result[1][0], 4) + " y="
+ IJ.d2s(result[1][1], 4));
}
} else {
for (i = 0; i < 2; i++)
for (j = 0; j < 2; j++)
result[i][j] = max2[i][j]; // result is [2][3], max2 is [2][2]
}
/*
* Calculate locations of the maximums on FFT (corresponding to the diagonals of
* the checkerboard pattern)
*/
double[][] maxOnFFT = { { 2 * size * (max2[0][0] - max2[1][0]), 2 * size * (max2[0][1] - max2[1][1]) },
{ 2 * size * (max2[0][0] + max2[1][0]), 2 * size * (max2[0][1] + max2[1][1]) } };
/*
* We have only one half of the FFT data so rotate 180-degrees around the center
* if the point is in the bottom half
*/
double[] maxPhases = new double[2];
boolean[] invertPhaseSign = { false, false };
int maxIndex; // iterate through the two maximums on FFT for phase measurement
int[][] interpolateXY = new int[2][2];
double[][] interpolateKxy = new double[2][2];
double[][][] interpolatePhases = new double[2][2][2];
double[][][] interpolateAmplitudes = new double[2][2][2];// Maybe use it? if the amplitudes are very different?
int ix, iy;
boolean phaseCorr; // phase shift before averaging, to prevent rollover
for (maxIndex = 0; maxIndex < 2; maxIndex++) {
if (maxOnFFT[maxIndex][1] < 0) {
invertPhaseSign[maxIndex] = true;
maxOnFFT[maxIndex][0] = -maxOnFFT[maxIndex][0];
maxOnFFT[maxIndex][1] = -maxOnFFT[maxIndex][1];
}
interpolateXY[maxIndex][1] = (int) maxOnFFT[maxIndex][1];
interpolateKxy[maxIndex][1] = maxOnFFT[maxIndex][1] - interpolateXY[maxIndex][1];
if (maxOnFFT[maxIndex][0] < 0) {
interpolateXY[maxIndex][0] = (int) (size + maxOnFFT[maxIndex][0]);
interpolateKxy[maxIndex][0] = (size + maxOnFFT[maxIndex][0]) - interpolateXY[maxIndex][0];
} else {
interpolateXY[maxIndex][0] = (int) maxOnFFT[maxIndex][0];
interpolateKxy[maxIndex][0] = maxOnFFT[maxIndex][0] - interpolateXY[maxIndex][0];
}
for (j = 0; j < 2; j++) {
ix = (interpolateXY[maxIndex][0] + j) % size;
for (i = 0; i < 2; i++) {
iy = interpolateXY[maxIndex][1] + i;
interpolateAmplitudes[maxIndex][i][j] = Math.sqrt(fft_complex[iy][ix][0] * fft_complex[iy][ix][0]
+ fft_complex[iy][ix][1] * fft_complex[iy][ix][1]);
interpolatePhases[maxIndex][i][j] = Math.atan2(
(invertPhaseSign[maxIndex] ? -1.0 : 1.0) * fft_complex[iy][ix][1], fft_complex[iy][ix][0]);
if (local_debug_level > 5) {
System.out.println("maxIndex=" + maxIndex + " ix=" + ix + " iy=" + iy + " phase="
+ IJ.d2s(interpolatePhases[maxIndex][i][j], 4) + " amplitude="
+ interpolateAmplitudes[maxIndex][i][j]);
}
}
}
phaseCorr = false;
if ((interpolatePhases[maxIndex][0][0] > Math.PI / 2) || (interpolatePhases[maxIndex][0][1] > Math.PI / 2)
|| (interpolatePhases[maxIndex][1][0] > Math.PI / 2)
|| (interpolatePhases[maxIndex][1][1] > Math.PI / 2)
|| (interpolatePhases[maxIndex][0][0] < -Math.PI / 2)
|| (interpolatePhases[maxIndex][0][1] < -Math.PI / 2)
|| (interpolatePhases[maxIndex][1][0] < -Math.PI / 2)
|| (interpolatePhases[maxIndex][1][1] < -Math.PI / 2)) {
phaseCorr = true;
interpolatePhases[maxIndex][0][0] += (interpolatePhases[maxIndex][0][0] < 0) ? Math.PI : -Math.PI;
interpolatePhases[maxIndex][0][1] += (interpolatePhases[maxIndex][0][1] < 0) ? Math.PI : -Math.PI;
interpolatePhases[maxIndex][1][0] += (interpolatePhases[maxIndex][1][0] < 0) ? Math.PI : -Math.PI;
interpolatePhases[maxIndex][1][1] += (interpolatePhases[maxIndex][1][1] < 0) ? Math.PI : -Math.PI;
if (local_debug_level > 5) {
System.out.println("Shifting phases by PI/2 before averaging (to avoid rollover)");
}
}
maxPhases[maxIndex] = interpolateKxy[maxIndex][1]
* (interpolateKxy[maxIndex][0] * interpolatePhases[maxIndex][1][1]
+ (1.0 - interpolateKxy[maxIndex][0]) * interpolatePhases[maxIndex][1][0])
+ (1.0 - interpolateKxy[maxIndex][1])
* (interpolateKxy[maxIndex][0] * interpolatePhases[maxIndex][0][1]
+ (1.0 - interpolateKxy[maxIndex][0]) * interpolatePhases[maxIndex][0][0]);
if (phaseCorr)
maxPhases[maxIndex] += (maxPhases[maxIndex] < 0) ? Math.PI : -Math.PI;
if (local_debug_level > 5) {
System.out.println("kx=" + IJ.d2s(interpolateKxy[maxIndex][0], 4) + " ky="
+ IJ.d2s(interpolateKxy[maxIndex][1], 4));
}
if (local_debug_level > 2) {
System.out.println("maxIndex=" + maxIndex + " phase=" + IJ.d2s(maxPhases[maxIndex], 4));
}
}
double[] checkerPhases = findCheckerPhases(max2,
maxPhases); /* may be different for greens==true . No, the same */
for (i = 0; i < 2; i++)
result[i][2] = checkerPhases[i];
if (local_debug_level > 2)
System.out.println();
return result;
}
// Above is currently used for years, below is for experiments
public double[][] findPattern(DoubleFHT doubleFHT, double[] input_pixels, // pixel array to process
int size, // FFT size
PatternDetectParameters patternDetectParameters, double min_half_period, double max_half_period,
boolean greens, // this is a pattern for combined greens (diagonal), adjust results accordingly
String title) { // title prefix to use for debug images
if (!patternDetectParameters.use_large_cells) {
return findPatternUsedNow(input_pixels, // pixel array to process
size, // FFT size
patternDetectParameters, greens, // this is a pattern for combined greens (diagonal), adjust results
// accordingly
title); // title prefix to use for debug images
}
int debug_threshold = 4;
// this.debugLevel = 11;
if (this.debugLevel > 5) {
System.out.println("findPattern(): this.debugLevel = " + this.debugLevel);
}
if (doubleFHT == null)
doubleFHT = new DoubleFHT(); // pass from the caller to re-use
double[] cpixels = input_pixels.clone();
if (min_half_period < 2) {
min_half_period = 2;
}
double[] dfht = new double[input_pixels.length]; // will save FHT from phase auto correlation
double[][] rslt = new double[2][3];
cpixels = doubleFHT.phaseCorrelate(cpixels, patternDetectParameters.phaseCoeff, 0,
patternDetectParameters.lowpass_sigma, dfht);
if (this.debugLevel > (debug_threshold + 1)) {
double[][] src_corr = { input_pixels, cpixels };
String[] titles = { "source", "corr" };
ShowDoubleFloatArrays.showArrays(src_corr, size, size, true,
"fft_corr-" + patternDetectParameters.phaseCoeff, titles);
}
int[][] half_periods = findFirst2MinOnCorrelation(cpixels, min_half_period, max_half_period);
if (half_periods != null) { // check vectors are not close to colinear
double sin = (half_periods[0][0] * half_periods[1][1] - half_periods[0][1] * half_periods[1][0])
/ Math.sqrt((half_periods[0][0] * half_periods[0][0] + half_periods[0][1] * half_periods[0][1])
* (half_periods[1][0] * half_periods[1][0] + half_periods[1][1] * half_periods[1][1]));
if (Math.abs(sin) < patternDetectParameters.min_sin) {
if (this.debugLevel > debug_threshold)
System.out.println("Half-period vectors on correlation are too close to colinear - sin=" + sin);
return null;
}
}
if (this.debugLevel > 7) {
if (half_periods == null) {
if (this.debugLevel > (debug_threshold + 0))
System.out.println("Could not find half-periods on correlation");
return null;
} else {
if (this.debugLevel > (debug_threshold + 1))
System.out.println("first half period (x/y) :" + half_periods[0][0] + "/" + half_periods[0][1]
+ ", second half period :" + half_periods[1][0] + "/" + half_periods[1][1]);
}
}
double[][] dhp = getPeriodsFromCorrelation(cpixels, half_periods, // a pair of half-periods
patternDetectParameters.min_frac, patternDetectParameters.no_crazy);
if (dhp != null) {
if (this.debugLevel > (debug_threshold + 1))
System.out.println(String.format("findPattern(): dhp= [[%.5f,%.5f],[%.5f,%.5f]]", dhp[0][0], dhp[0][1],
dhp[1][0], dhp[1][1]));
} else {
if (this.debugLevel > (debug_threshold + 0))
System.out.println("findPattern(): getPeriodsFromCorrelation() FAILED");
}
/* these are combined greens, convert vectors to original pixel space)! */
if (greens) {
double[][] rotMatrix = { { 1.0, -1.0 }, { 1.0, 1.0 } };
double[][] max2orig = matrix2x2_mul(dhp, rotMatrix);
for (int i = 0; i < 2; i++)
for (int j = 0; j < 2; j++)
rslt[i][j] = max2orig[i][j]; // result is [2][3], max2orig is [2][2]
} else {
for (int i = 0; i < 2; i++)
for (int j = 0; j < 2; j++)
rslt[i][j] = 2.0 * dhp[i][j]; // result is [2][3], max2 is [2][2]
}
if (this.debugLevel > (debug_threshold + 0)) {
System.out.println(
"Corrected to original pixels[0] x=" + IJ.d2s(rslt[0][0], 4) + " y=" + IJ.d2s(rslt[0][1], 4));
System.out.println(
"Corrected to original pixels[1] x=" + IJ.d2s(rslt[1][0], 4) + " y=" + IJ.d2s(rslt[1][1], 4));
}
// Calculate complex FFT - needed to determine pattern phases (autocorrelation
// does not provide this
// dfht array has FHT(input_pixels) save from calculation of the phase
// autocorrelation
double[][][] fft_cmplx = doubleFHT.FHT2FFTHalf(dfht, size);
if (this.debugLevel > (debug_threshold + 2)) {
ShowDoubleFloatArrays.showComplex(fft_cmplx, "fft_cmplx");
}
/*
* Calculate locations of the maximums on FFT (corresponding to the diagonals of
* the checkerboard pattern)
*/
double[][] max_on_FFT = { { 2 * size * (dhp[0][0] - dhp[1][0]), 2 * size * (dhp[0][1] - dhp[1][1]) },
{ 2 * size * (dhp[0][0] + dhp[1][0]), 2 * size * (dhp[0][1] + dhp[1][1]) } };
/*
* We have only one half of the FFT data so rotate 180-degrees around the center
* if the point is in the bottom half
*/
double[] max_phases = getPatternPhasesFromFFT(fft_cmplx, size, max_on_FFT);
double[] checker_phases = findCheckerPhases(dhp,
max_phases); /* may be different for greens==true . No, the same */
for (int i = 0; i < 2; i++)
rslt[i][2] = checker_phases[i];
if (this.debugLevel > (debug_threshold + 0))
System.out.println();
return rslt;
}
/* ======================================================================== */
// calculate pattern 3D phases by interpolating im/re for the found maximums
private double[] getPatternPhasesFromFFT(double[][][] fft_complex, int size, double[][] maxOnFFT) {
double[] maxPhases = new double[2];
boolean[] invertPhaseSign = { false, false };
int maxIndex; // iterate through the two maximums on FFT for phase measurement
int[][] interpolateXY = new int[2][2];
double[][] interpolateKxy = new double[2][2];
double[][][] interpolatePhases = new double[2][2][2];
double[][][] interpolateAmplitudes = new double[2][2][2];// Maybe use it? if the amplitudes are very different?
int ix, iy;
boolean phaseCorr; // phase shift before averaging, to prevent rollover
for (maxIndex = 0; maxIndex < 2; maxIndex++) {
if (maxOnFFT[maxIndex][1] < 0) {
invertPhaseSign[maxIndex] = true;
maxOnFFT[maxIndex][0] = -maxOnFFT[maxIndex][0];
maxOnFFT[maxIndex][1] = -maxOnFFT[maxIndex][1];
}
interpolateXY[maxIndex][1] = (int) maxOnFFT[maxIndex][1];
interpolateKxy[maxIndex][1] = maxOnFFT[maxIndex][1] - interpolateXY[maxIndex][1];
if (maxOnFFT[maxIndex][0] < 0) {
interpolateXY[maxIndex][0] = (int) (size + maxOnFFT[maxIndex][0]);
interpolateKxy[maxIndex][0] = (size + maxOnFFT[maxIndex][0]) - interpolateXY[maxIndex][0];
} else {
interpolateXY[maxIndex][0] = (int) maxOnFFT[maxIndex][0];
interpolateKxy[maxIndex][0] = maxOnFFT[maxIndex][0] - interpolateXY[maxIndex][0];
}
for (int j = 0; j < 2; j++) {
ix = (interpolateXY[maxIndex][0] + j) % size;
for (int i = 0; i < 2; i++) {
iy = interpolateXY[maxIndex][1] + i; // next: OOB 2147483647
if (iy >= fft_complex.length) {
iy = fft_complex.length - 1; // was Index 17 out of bounds for length 17
}
interpolateAmplitudes[maxIndex][i][j] = Math.sqrt(fft_complex[iy][ix][0] * fft_complex[iy][ix][0]
+ fft_complex[iy][ix][1] * fft_complex[iy][ix][1]);
interpolatePhases[maxIndex][i][j] = Math.atan2(
(invertPhaseSign[maxIndex] ? -1.0 : 1.0) * fft_complex[iy][ix][1], fft_complex[iy][ix][0]);
if (this.debugLevel > 5) {
System.out.println("maxIndex=" + maxIndex + " ix=" + ix + " iy=" + iy + " phase="
+ IJ.d2s(interpolatePhases[maxIndex][i][j], 4) + " amplitude="
+ interpolateAmplitudes[maxIndex][i][j]);
}
}
}
phaseCorr = false;
if ((interpolatePhases[maxIndex][0][0] > Math.PI / 2) || (interpolatePhases[maxIndex][0][1] > Math.PI / 2)
|| (interpolatePhases[maxIndex][1][0] > Math.PI / 2)
|| (interpolatePhases[maxIndex][1][1] > Math.PI / 2)
|| (interpolatePhases[maxIndex][0][0] < -Math.PI / 2)
|| (interpolatePhases[maxIndex][0][1] < -Math.PI / 2)
|| (interpolatePhases[maxIndex][1][0] < -Math.PI / 2)
|| (interpolatePhases[maxIndex][1][1] < -Math.PI / 2)) {
phaseCorr = true;
interpolatePhases[maxIndex][0][0] += (interpolatePhases[maxIndex][0][0] < 0) ? Math.PI : -Math.PI;
interpolatePhases[maxIndex][0][1] += (interpolatePhases[maxIndex][0][1] < 0) ? Math.PI : -Math.PI;
interpolatePhases[maxIndex][1][0] += (interpolatePhases[maxIndex][1][0] < 0) ? Math.PI : -Math.PI;
interpolatePhases[maxIndex][1][1] += (interpolatePhases[maxIndex][1][1] < 0) ? Math.PI : -Math.PI;
if (this.debugLevel > 5) {
System.out.println("Shifting phases by PI/2 before averaging (to avoid rollover)");
}
}
maxPhases[maxIndex] = interpolateKxy[maxIndex][1]
* (interpolateKxy[maxIndex][0] * interpolatePhases[maxIndex][1][1]
+ (1.0 - interpolateKxy[maxIndex][0]) * interpolatePhases[maxIndex][1][0])
+ (1.0 - interpolateKxy[maxIndex][1])
* (interpolateKxy[maxIndex][0] * interpolatePhases[maxIndex][0][1]
+ (1.0 - interpolateKxy[maxIndex][0]) * interpolatePhases[maxIndex][0][0]);
if (phaseCorr)
maxPhases[maxIndex] += (maxPhases[maxIndex] < 0) ? Math.PI : -Math.PI;
if (this.debugLevel > 5) {
System.out.println("kx=" + IJ.d2s(interpolateKxy[maxIndex][0], 4) + " ky="
+ IJ.d2s(interpolateKxy[maxIndex][1], 4));
}
if (this.debugLevel > 2) {
System.out.println("maxIndex=" + maxIndex + " phase=" + IJ.d2s(maxPhases[maxIndex], 4));
}
}
return maxPhases;
}
/* ======================================================================== */
// zero is in the center
private int[][] findFirst2MinOnCorrelation(double[] pixels, double min_half_period, // from white to black
double max_half_period) { // from white to black
double min_fract_period = 0.3; // second minimum should be not closer to the first than this fraction of the
// first
// distance from zero
int size = (int) Math.sqrt(pixels.length);
int size2 = size >> 1;
if (max_half_period <= 0)
max_half_period = size;
if (min_half_period < 1)
min_half_period = 1;
double mn2 = min_half_period * min_half_period;
double mx2 = max_half_period * max_half_period;
int[][] mins = { { 0, 0 }, { 0, 0 } };
double mn = 0.0; // minimums are always negative
// find the first minimum
for (int row = 0; row <= size2; row++) {
double dy2 = (row - size2) * (row - size2);
for (int col = 0; col < size; col++) {
double d2 = (col - size2) * (col - size2) + dy2;
if ((d2 >= mn2) && (d2 < mx2) && (pixels[row * size + col] < mn)) {
mn = pixels[row * size + col];
mins[0][0] = col - size2;
mins[0][1] = row - size2; // always <=0
}
}
}
if (mn == 0.0) {
return null; // could not find a first minimum
}
// find the second minimum (not too close to the first
int x0 = mins[0][0] + size2;
int y0 = mins[0][1] + size2;
int x1 = size - x0;
int y1 = size - y0;
mn = 0.0;
double mn2a = Math.max(mn2, min_fract_period * (mins[0][0] * mins[0][0] + mins[0][1] * mins[0][1]));
for (int row = 0; row <= size2; row++) {
double dy2 = (row - size2) * (row - size2);
for (int col = 0; col < size; col++) {
double d2 = (col - size2) * (col - size2) + dy2;
if ((d2 >= mn2) && (d2 < mx2) && (pixels[row * size + col] < mn)) {
// verify it is far enough from the first minimum and it's mirror
d2 = (row - y0) * (row - y0) + (col - x0) * (col - x0);
if (d2 >= 2 * mn2a) { // diagonal
// verify it is far enough from the mirrored first
d2 = (row - y1) * (row - y1) + (col - x1) * (col - x1);
if (d2 >= 2 * mn2a) { // diagonal
mn = pixels[row * size + col];
mins[1][0] = col - size2;
mins[1][1] = row - size2; // always <=0
}
}
}
}
}
if (mn == 0.0) {
return null; // could not find a second minimum
}
return mins;
}
private int rad_search(int[][] half_periods) {
int ahp;
int amx;
int dist = -1;
for (int n = 0; n < half_periods.length; n++) {
amx = 0;
for (int i = 0; i < half_periods[0].length; i++) {
ahp = half_periods[n][i];
if (ahp < 0)
ahp = -ahp;
if (ahp > amx)
amx = ahp;
}
if ((dist < 0) || (dist > amx))
dist = amx;
}
if (dist < 3)
return 1;
return (dist - 1) >> 1;
}
private boolean isLocalMinMax(double[] pixels, int size, int indx, boolean need_min) {
int sizesize = size * size;
if ((indx < 0) || (indx >= sizesize)) {
if (this.debugLevel > 0) {
System.out.println("isLocalMinMax(): bad indx = " + indx);
}
return false;
}
for (int dy = -size; dy <= size; dy += size) {
for (int dx = -1; dx <= 1; dx++) {
int indx1 = indx + dy + dx;
if ((indx1 >= 0) && (indx1 < sizesize)) {
if (need_min) {
if (pixels[indx] > pixels[indx1])
return false;
} else {
if (pixels[indx] < pixels[indx1])
return false;
}
}
}
}
return true;
}
private double[][] getPeriodsFromCorrelation(double[] pixels, int[][] half_periods, // a pair of half-periods
double min_frac, // minimal fraction of the center maximum to consider
boolean no_crazy) { // discard min/max with failed interpolation
// how far to look around expected maximum/minimum
double[][] rslt = null;
int dbg_threshold = 3;
int search_radius = rad_search(half_periods);
// below first >= second
int[][] search_order = { { 1, 0 }, // just half periods, negative
{ 1, 1 }, // diagonals, positive
{ 2, 0 }, // full period, positive
{ 2, 1 }, // 8 (4) directions, negative
{ 2, 2 } }; // positive
int size = (int) Math.sqrt(pixels.length);
int size2 = size / 2;
int[][] aper = new int[2][2];
double[][] dhalf_periods = new double[2][2]; // will refine half_periods;
for (int i = 0; i < 2; i++)
for (int j = 0; j < 2; j++)
aper[i][j] = Math.abs(half_periods[i][j]);
for (int i = 0; i < 2; i++)
for (int j = 0; j < 2; j++)
dhalf_periods[i][j] = half_periods[i][j];
double min_avalue = pixels[size2 * (size + 1)] * min_frac; // minimal absolute value of min/max
int max_dist = size2 - 2; // arbitrary ??
int nord = 0; // to preserve after loop
layers_iteration: for (nord = 0; nord < search_order.length; nord++) {
int[] xy_ind = search_order[nord];
// 1 check if too far to fit
if (((xy_ind[0] * aper[0][0] + xy_ind[1] * aper[1][0]) > max_dist)
|| ((xy_ind[0] * aper[0][1] + xy_ind[1] * aper[1][1]) > max_dist)
|| ((xy_ind[1] * aper[0][0] + xy_ind[0] * aper[1][0]) > max_dist)
|| ((xy_ind[1] * aper[0][1] + xy_ind[0] * aper[1][1]) > max_dist)) {
break layers_iteration;
}
boolean are_mins = ((xy_ind[0] + xy_ind[1]) % 2) != 0;
int nspots = ((xy_ind[1] == 0) || (xy_ind[0] == xy_ind[1])) ? 2 : 4;
// double [][] predicted_centers = new double nsamples
int[][] lin_comb = new int[nspots][2]; // number of half periods (incl. negative) to combine
if (xy_ind[1] == 0) {
lin_comb[0][0] = xy_ind[0]; // first spot, first vector
lin_comb[0][1] = 0; // first spot, second vector
lin_comb[1][0] = 0; // second spot, first vector
lin_comb[1][1] = xy_ind[0]; // second spot, second vector
} else if (xy_ind[0] == xy_ind[1]) {
lin_comb[0][0] = xy_ind[0]; // first spot, first vector
lin_comb[0][1] = xy_ind[0]; // first spot, second vector
lin_comb[1][0] = xy_ind[0]; // second spot, first vector
lin_comb[1][1] = -xy_ind[0]; // second spot, second vector
} else {
lin_comb[0][0] = xy_ind[0]; // first spot, first vector
lin_comb[0][1] = xy_ind[1]; // first spot, second vector
lin_comb[1][0] = xy_ind[0]; // second spot, first vector
lin_comb[1][1] = -xy_ind[1]; // second spot, second vector
lin_comb[2][0] = xy_ind[1]; // third spot, first vector
lin_comb[2][1] = xy_ind[0]; // third spot, second vector
lin_comb[3][0] = -xy_ind[1]; // fourth spot, first vector
lin_comb[3][1] = xy_ind[0]; // fourth spot, second vector
}
double[][] spot_centers = new double[nspots][2];
// iterate through all min/max spots in a layer, break outer loop on any failure
// (and use previous layer data)
for (int nspot = 0; nspot < nspots; nspot++) {
double[] xyc = { lin_comb[nspot][0] * dhalf_periods[0][0] + lin_comb[nspot][1] * dhalf_periods[1][0],
lin_comb[nspot][0] * dhalf_periods[0][1] + lin_comb[nspot][1] * dhalf_periods[1][1] };
int[] ixyc = { (int) Math.round(xyc[0]), (int) Math.round(xyc[1]) };
if ((ixyc[0] < 0) || (ixyc[1] < 0) || (ixyc[0] >= size) || (ixyc[0] >= size)) {
break layers_iteration;
}
int[] ixym = ixyc.clone();
for (int y = ixyc[1] - search_radius; y <= ixyc[1] + search_radius; y++)
if ((y >= -max_dist) && (y < max_dist)) {
for (int x = ixyc[0] - search_radius; x <= ixyc[0] + search_radius; x++)
if ((x >= -max_dist) && (x < max_dist)) {
double d = pixels[(y + size2) * size + (x + size2)]
- pixels[(ixym[1] + size2) * size + (ixym[0] + size2)];
if (are_mins)
d = -d;
if (d > 0) {
ixym[0] = x;
ixym[1] = y;
}
}
}
// verify it is local max/min
if (!isLocalMinMax(pixels, size, (ixym[1] + size2) * size + (ixym[0] + size2), are_mins)) {
if (this.debugLevel > dbg_threshold) {
System.out.println("getPeriodsFromCorrelation(): is not a local max/min, nord=" + nord);
}
break layers_iteration;
}
// verify it is strong enough
if (Math.abs(pixels[(ixym[1] + size2) * size + (ixym[0] + size2)]) < min_avalue) { // BUG: -8030
if (this.debugLevel > dbg_threshold) {
System.out.println("getPeriodsFromCorrelation(): min/max is too weak: "
+ pixels[(ixym[1] + size2) * size + (ixym[0] + size2)] + " < " + min_avalue + " (layer"
+ nord + ")");
}
break layers_iteration;
}
// use quadratic interpolation to find min/max
double[][][] data = new double[9][3][];
int index = 0;
for (int dy = -1; dy <= 1; dy++) {
for (int dx = -1; dx <= 1; dx++) {
data[index][0] = new double[2];
data[index][1] = new double[1];
data[index][2] = new double[1];
int pix_indx = (ixym[1] + dy + size2) * size + (ixym[0] + dx + size2);
if ((pix_indx >= 0) && (pix_indx < pixels.length)) {
data[index][0][0] = dx;
data[index][0][1] = dy;
data[index][1][0] = are_mins ? -pixels[pix_indx] : pixels[pix_indx];
data[index][2][0] = 1.0;
} else {
data[index][2][0] = 0.0;
}
index++;
}
}
double[] corrXY = (new PolynomialApproximation()).quadraticMax2d(data);
if (corrXY == null) {
if (no_crazy) {
if (this.debugLevel > dbg_threshold) {
System.out.println(
"getPeriodsFromCorrelation(): interpolation failed," + " (layer" + nord + ")");
}
break layers_iteration;
}
corrXY = new double[2];
corrXY[0] = 0.0;
corrXY[1] = 0.0;
}
// verify that interpolation did not go crazy. If it did - reset to center
if ((corrXY[0] > 1.5) || (corrXY[1] > 1.5) || (corrXY[0] < -1.5) || (corrXY[1] < -1.5)) {
if (no_crazy) {
if (this.debugLevel > dbg_threshold) {
System.out.println("getPeriodsFromCorrelation(): interpolation failed, " + "corrXY = ["
+ corrXY[0] + ", " + corrXY[1] + "] (layer" + nord + ")");
}
break layers_iteration;
}
corrXY[0] = 0.0;
corrXY[1] = 0.0;
}
spot_centers[nspot][0] = ixym[0] + corrXY[0];
spot_centers[nspot][1] = ixym[1] + corrXY[1];
}
// refine half-period values
if (xy_ind[1] == 0) {
dhalf_periods[0][0] = spot_centers[0][0] / xy_ind[0];
dhalf_periods[0][1] = spot_centers[0][1] / xy_ind[0];
dhalf_periods[1][0] = spot_centers[1][0] / xy_ind[0];
dhalf_periods[1][1] = spot_centers[1][1] / xy_ind[0];
} else if (xy_ind[0] == xy_ind[1]) {
dhalf_periods[0][0] = (spot_centers[0][0] + spot_centers[1][0]) / 2 / xy_ind[0];
dhalf_periods[0][1] = (spot_centers[0][1] + spot_centers[1][1]) / 2 / xy_ind[0];
dhalf_periods[1][0] = (spot_centers[0][0] - spot_centers[1][0]) / 2 / xy_ind[0];
dhalf_periods[1][1] = (spot_centers[0][1] - spot_centers[1][1]) / 2 / xy_ind[0];
} else {
dhalf_periods[0][0] = (spot_centers[0][0] + spot_centers[1][0] + spot_centers[2][0]
- spot_centers[3][0]) / (xy_ind[0] + xy_ind[1]) / 2;
dhalf_periods[0][1] = (spot_centers[0][1] + spot_centers[1][1] + spot_centers[2][1]
- spot_centers[3][1]) / (xy_ind[0] + xy_ind[1]) / 2;
dhalf_periods[1][0] = (spot_centers[0][0] - spot_centers[1][0] + spot_centers[2][0]
+ spot_centers[3][0]) / (xy_ind[0] + xy_ind[1]) / 2;
dhalf_periods[1][1] = (spot_centers[0][1] - spot_centers[1][1] + spot_centers[2][1]
+ spot_centers[3][1]) / (xy_ind[0] + xy_ind[1]) / 2;
}
} // for (int nord = 0; nord < search_order.length; nord++) {
// double [][] pattern_diagonals = {
// {dhalf_periods[0][0]+dhalf_periods[1][0],
// dhalf_periods[0][1]+dhalf_periods[1][1]},
// {dhalf_periods[0][0]-dhalf_periods[1][0],
// dhalf_periods[0][1]-dhalf_periods[1][1]}};
double[][] scaled_v = new double[2][2];
double k_scale = 4.0;
for (int i = 0; i < 2; i++)
for (int j = 0; j < 2; j++) {
scaled_v[i][j] = k_scale * dhalf_periods[i][j]; // not clear why 4 and not 2
}
// rslt = vectToWaveVect(pattern_diagonals);
// make vectors to have positive y:
for (int i = 0; i < 2; i++) {
if (scaled_v[i][1] < 0) {
scaled_v[i][0] = -scaled_v[i][0];
scaled_v[i][1] = -scaled_v[i][1];
}
}
rslt = vectToWaveVect(scaled_v);
// rslt = dhalf_periods;
// sort so first vector to second vector will be clockwise (positive y is
// downwards)
{
int j = 0, k = 1;
if ((rslt[j][0] * rslt[k][1] - rslt[k][0] * rslt[j][1]) < 0) {
double[] tmp = rslt[0];
rslt[0] = rslt[1];
rslt[1] = tmp;
}
}
for (int i = 0; i < 2; i++)
for (int j = 0; j < 2; j++) {
if (Double.isNaN(rslt[i][j]) || Double.isInfinite(rslt[i][j])) {
System.out.println("got NaN/Infinity, nord = " + nord);
return null;
}
}
return rslt;
}
// vect[0][] - 1-st vector connecting nodes, vect[1][] - second vector
// return [0][] - first wave vector, orthogonal to vecgt[0][], return [1][] - to
// the second vect[1][]
double[][] vectToWaveVect(double[][] vect) {
double a = 1.0 / (vect[0][0] * vect[1][1] - vect[0][1] * vect[1][0]);
// double [] rv0 = {-vect[0][1], vect[0][0]};
// double [] rv1 = { vect[1][1], -vect[1][0]};
// double [][] wv= {{a*rv0[0], a*rv0[1]},{a*rv1[0], a*rv1[1]}};
double[][] wv = { { -a * vect[0][1], a * vect[0][0] }, { a * vect[1][1], -a * vect[1][0] } };
return wv;
}
private int[][] findFirst2MaxOnSpectrum(double[][][] pixels, // complex, top half, starting from 0,0
/*
* May need to reduce the skip_around to be able to handle smaller number of
* pattern periods? Or re-try if failed? Guess somehow?
*/
int skip_around, // skip +- from (0,0) and previous max
double minOrtho) { // 0.5 - 30deg. orthogonality of 2 vectors - 1.0 - perpendicular, 0.0 - parallel
int[][] max2 = { { 0, 0 }, { 0, 0 } };
double thisMax = 0.0;
int x, y, sx1, sx2;
double p, a;
/* find first point */
for (y = 0; y < pixels.length; y++)
for (x = 0; x < pixels[0].length; x++) {
p = pixels[y][x][0] * pixels[y][x][0] + pixels[y][x][1] * pixels[y][x][1];
if (p > thisMax) {
if ((y <= skip_around) && ((x <= skip_around) || (x >= pixels[0].length - skip_around)))
continue; /* too close to [0,0] */
max2[0][0] = x;
max2[0][1] = y;
thisMax = p;
}
}
thisMax = 0.0;
sx1 = (max2[0][0] > (pixels[0].length / 2)) ? (max2[0][0] - pixels[0].length)
: max2[0][0]; /* y is always positive here */
/* find second point */
// Maybe also check if it is a local maximum (not on the border with protected
// area (0, first point, y=0, ...) ?
for (y = 0; y < pixels.length; y++)
for (x = 0; x < pixels[0].length; x++) {
p = pixels[y][x][0] * pixels[y][x][0] + pixels[y][x][1] * pixels[y][x][1];
if (p > thisMax) {
/* Is this a local maximum? */
if (y > 0) {
if (p < (pixels[y - 1][x][0] * pixels[y - 1][x][0] + pixels[y - 1][x][1] * pixels[y - 1][x][1]))
continue;
if ((x > 0) && (p < (pixels[y - 1][x - 1][0] * pixels[y - 1][x - 1][0]
+ pixels[y - 1][x - 1][1] * pixels[y - 1][x - 1][1])))
continue;
if ((x < (pixels[0].length - 1)) && (p < (pixels[y - 1][x + 1][0] * pixels[y - 1][x + 1][0]
+ pixels[y - 1][x + 1][1] * pixels[y - 1][x + 1][1])))
continue;
}
if (y < (pixels.length - 1)) {
if (p < (pixels[y + 1][x][0] * pixels[y + 1][x][0] + pixels[y + 1][x][1] * pixels[y + 1][x][1]))
continue;
if ((x > 0) && (p < (pixels[y + 1][x - 1][0] * pixels[y + 1][x - 1][0]
+ pixels[y + 1][x - 1][1] * pixels[y + 1][x - 1][1])))
continue;
if ((x < (pixels[0].length - 1)) && (p < (pixels[y + 1][x + 1][0] * pixels[y + 1][x + 1][0]
+ pixels[y + 1][x + 1][1] * pixels[y + 1][x + 1][1])))
continue;
}
if ((x > 0) && (p < (pixels[y][x - 1][0] * pixels[y][x - 1][0]
+ pixels[y][x - 1][1] * pixels[y][x - 1][1])))
continue;
if ((x < (pixels[0].length - 1)) && (p < (pixels[y][x + 1][0] * pixels[y][x + 1][0]
+ pixels[y][x + 1][1] * pixels[y][x + 1][1])))
continue;
if ((y <= skip_around) && ((x <= skip_around) || (x >= pixels[0].length - skip_around))) {
if (this.debugLevel > 5) {
System.out.println("rejecting point [" + x + "," + y + "] it is too close to [0,0]");
}
continue; /* too close to [0,0] */
}
if ((y <= skip_around) && ((x <= skip_around) || (x >= pixels[0].length - skip_around))) {
if (this.debugLevel > 5) {
System.out.println("rejecting point [" + x + "," + y + "] it is too close to [0,0]");
}
continue; /* too close to [0,0] */
}
if (((y <= (max2[0][1] + skip_around)) && (y >= (max2[0][1] - skip_around)))
&& (((x <= (max2[0][0] + skip_around)) && (x >= (max2[0][0] - skip_around)))
|| (x >= (max2[0][0] + pixels[0].length - skip_around))
|| (x <= (max2[0][0] - pixels[0].length + skip_around)))) {
if (this.debugLevel > 5) {
System.out.println(
"rejecting point [" + x + "," + y + "] as it is too close to the first one - ["
+ max2[0][0] + "(" + sx1 + ")," + max2[0][1] + "]");
}
continue; /* too close to first maximum */
}
sx2 = (x > (pixels[0].length / 2)) ? (x - pixels[0].length) : x;
a = (sx1 * y - max2[0][1] * sx2);
a = a * a / (sx1 * sx1 + max2[0][1] * max2[0][1]) / (sx2 * sx2 + y * y);
if (a < (minOrtho * minOrtho)) { /* vectors are too close to parallel */
if (this.debugLevel > 5) {
System.out.println("rejecting point [" + x + "(" + sx2 + ")," + y
+ "] as the vector is too close to the first one - [" + max2[0][0] + "(" + sx1
+ ")," + max2[0][1] + "]");
System.out.println(
"pixels.length=" + pixels.length + " pixels[0].length=" + pixels[0].length);
}
continue;
}
max2[1][0] = x;
max2[1][1] = y;
thisMax = p;
}
}
if (thisMax == 0.0) {
/* TODO: THat really happens on the real data */
System.out.println("Failed to find a second maximum");
return null;
}
if (max2[0][0] > (pixels[0].length / 2))
max2[0][0] -= pixels[0].length;
if (max2[1][0] > (pixels[0].length / 2))
max2[1][0] -= pixels[0].length;
return max2;
}
/* ======================================================================== */
/*
* Can it handle negative y if the refined maximum goes there? (maximal value on
* positive Y)
*/
private double[][] findFirst2MaxOnCorrelation(double[] pixels, int[][] startPoints,
PatternDetectParameters patternDetectParameters) {
double reasonbleFrequency = 2.0; // reject frequencies below that
int size = (int) Math.sqrt(pixels.length);
int[][] imax = startPoints.clone();
int[][] imax2 = new int[2 * patternDetectParameters.multiplesToTry][2];
boolean[] maxDefined = new boolean[2 * patternDetectParameters.multiplesToTry]; // .multiplesToTry = 4;
double[] maxValues = new double[startPoints.length];
double[] max2Values = new double[2];
double[][] max2 = new double[2 * patternDetectParameters.multiplesToTry][2];
int lim = size / 2 - 2; // safety measure
int nmax = 0;
int x = 1;
int y = 0;
int indx;
int[] dirs = { -1, -size - 1, -size, -size + 1, 1, size + 1, size, size - 1 };
boolean isMax;
int i, j, k, xmn, xmx, ymn, ymx;
int halfSize = size / 2;
double[] vlengths = { Math.sqrt(startPoints[0][0] * startPoints[0][0] + startPoints[0][1] * startPoints[0][1]),
Math.sqrt(startPoints[1][0] * startPoints[1][0] + startPoints[1][1] * startPoints[1][1]) };
boolean tooFar = false;
int numVect;
/* Look for the maximal values around startPoints (+/-diff_spectr_corr ) */
for (nmax = 0; nmax < startPoints.length; nmax++) {
ymn = imax[nmax][1] - patternDetectParameters.diffSpectrCorr; // .diffSpectrCorr=2
ymx = imax[nmax][1] + patternDetectParameters.diffSpectrCorr;
if (ymx > lim)
ymx = lim;
xmn = imax[nmax][0] - patternDetectParameters.diffSpectrCorr;
if (xmn < -lim)
xmx = -lim;
xmx = imax[nmax][0] + patternDetectParameters.diffSpectrCorr;
if (xmx > lim)
xmx = lim;
indx = (size + 1) * size / 2 + imax[nmax][1] * size + imax[nmax][0];
if ((Math.abs(imax[nmax][0]) > lim) || (Math.abs(imax[nmax][1]) > lim)) {
if (this.debugLevel > 2) {
System.out.println("Bad start point imax[0][0]=" + imax[0][0] + " imax[0][1]=" + imax[0][1]
+ " imax[1][0]=" + imax[1][0] + " imax[1][1]=" + imax[1][1] + " lim=" + lim);
}
return null;
}
// if ((indx<0) || (indx>=pixels.length)) {
// System.out.println(" imax[0][0]="+imax[0][0]+" imax[0][1]="+imax[0][1]+"
// imax[1][0]="+imax[1][0]+" imax[1][1]="+imax[1][1]+ " lim="+lim);
// }
maxValues[nmax] = pixels[indx];
for (y = ymn; y <= ymx; y++)
for (x = xmn; x <= xmx; x++) {
indx = (size + 1) * size / 2 + y * size + x;
if (pixels[indx] > maxValues[nmax]) {
/* Make sure it is closer to this point than to any other or [0,0] */
tooFar = false;
for (numVect = 0; numVect < startPoints.length; numVect++) {
if (Math.abs((x - imax[nmax][0]) * imax[numVect][0]
+ (y - imax[nmax][1]) * imax[numVect][1]) > 0.5 * vlengths[nmax]
* vlengths[numVect]) {
tooFar = true;
break;
}
}
if (tooFar) {
if (this.debugLevel > 5) {
System.out.println("rejecting point [" + x + "," + y
+ "] as the vector is closer to other max than [" + imax[nmax][0] + ","
+ imax[nmax][0] + "]" + " in the (+/-) direction: [" + imax[numVect][0] + ","
+ imax[numVect][0] + "]");
}
continue;
}
maxValues[nmax] = pixels[indx];
imax[nmax][0] = x;
imax[nmax][1] = y;
}
}
/* Make sure the maximum in the scanned area is also a local maximum */
isMax = true;
indx = (size + 1) * size / 2 + imax[nmax][1] * size + imax[nmax][0];
for (j = 0; j < 7; j++)
if (pixels[indx] < pixels[indx + dirs[j]]) {
isMax = false;
break;
}
if (!isMax) {
if (this.debugLevel > 2) {
System.out.println("This should not happen:");
System.out.println(
"Maximum is not a local maximum - BUG or consider changing patternDetectParameters.diffSpectrCorr="
+ patternDetectParameters.diffSpectrCorr);
System.out.println("point #" + (nmax + 1) + " (of 2), x0=" + startPoints[nmax][0] + " y0="
+ startPoints[nmax][1] + " x=" + imax[nmax][0] + " y=" + imax[nmax][1]);
}
/* Maybe return from here with null? */
while (!isMax && (indx > 0) && (indx > pixels.length)) {
isMax = true;
for (j = 0; j < 7; j++)
if (pixels[indx] < pixels[indx + dirs[j]]) {
isMax = false;
indx += dirs[j];
imax[nmax][1] = (indx / size) - halfSize;
imax[nmax][0] = (indx % size) - halfSize;
break;
}
}
if (!isMax) {
if (this.debugLevel > 2) {
System.out.println("Maximum still not reached, bailing out");
System.out.println("point #" + (nmax + 1) + " (of 2), x0=" + startPoints[nmax][0] + " y0="
+ startPoints[nmax][1] + " x=" + imax[nmax][0] + " y=" + imax[nmax][1]);
}
return null;
} else {
if (this.debugLevel > 2) {
System.out.println("point #" + (nmax + 1) + " (of 2), corrected local maximum is x="
+ imax[nmax][0] + " y=" + imax[nmax][1]);
}
}
}
}
/*
* Sort maximums so first vector to second vector will be clockwise (positive y
* is downwards)
*/
j = 0;
k = 1;
if ((imax[j][0] * imax[k][1] - imax[k][0] * imax[j][1]) < 0) {
j = 1;
k = 0;
}
imax2[0][0] = imax[j][0];
imax2[0][1] = imax[j][1];
imax2[1][0] = imax[k][0];
imax2[1][1] = imax[k][1];
/*
* Now define maximal radius of cluster (~0.7 of the average distance from 0,0
* to the 2 start points
*/
int maxX2Y2 = 0;
for (i = 0; i < 2; i++)
for (j = 0; j < 2; j++)
maxX2Y2 += imax2[i][j] * imax2[i][j];
maxX2Y2 /= 4;
// System.out.println("maxX2Y2="+maxX2Y2);
for (i = 0; i < 2 * patternDetectParameters.multiplesToTry; i++)
maxDefined[i] = (i < 2);
nmax = 2 * patternDetectParameters.multiplesToTry; /* but only the first two are known by now */
for (i = 0; i < 2; i++) {
if (this.debugLevel > 5)
System.out.println("i=" + i + " x=" + imax2[i][0] + " y=" + imax2[i][1] + " value=" + max2Values[i]);
}
int clusterNumber;
List pixelList = new ArrayList(100);
Integer Index, NewIndex, HillIndex;
double cx, cy, cm, minInCluster, f;
int[] clusterMap = new int[pixels.length];
for (i = 0; i < clusterMap.length; i++)
clusterMap[i] = 0; /// 0 - unused, -1 - "do not use"
int listIndex;
boolean noHills;
int clusterSize;
boolean isLocalMax;
int pair;
for (clusterNumber = 0; clusterNumber < nmax; clusterNumber++) {
pair = clusterNumber / 2 + 1;
if (!maxDefined[clusterNumber] && maxDefined[clusterNumber - 2]) {
/*
* We do not know the seed for this maximum, but the previous (of the same
* direction) may be known
*/
x = (int) (max2[clusterNumber - 2][0] * pair) / (pair - 1);
y = (int) (max2[clusterNumber - 2][1] * pair) / (pair - 1);
if ((x > (-lim + 1)) && (x < (lim - 1)) && (y > (-lim + 1)) && (y < (lim - 1))) {
Index = (y + halfSize) * size + x + halfSize;
/*
* there should be local maximum not more than "deviationSteps" steps from the
* x,y
*/
isLocalMax = false;
i = patternDetectParameters.deviationSteps;
while ((i > 0) && !isLocalMax) {
isLocalMax = true;
for (j = 0; j < dirs.length; j++) {
NewIndex = Index + dirs[j];
if ((NewIndex >= 0) && (NewIndex < clusterMap.length)
&& (pixels[NewIndex] > pixels[Index])) {
isLocalMax = false;
Index = NewIndex;
i--;
if (this.debugLevel > 5)
System.out.println("i=" + i + " x=" + ((Index % size) - halfSize) + " y="
+ ((Index / size) - halfSize) + " value=" + pixels[Index]);
break;
}
}
}
if (isLocalMax && (clusterMap[Index] == 0)) { // not yet used
imax2[clusterNumber][0] = (Index % size) - halfSize;
imax2[clusterNumber][1] = (Index / size) - halfSize;
maxDefined[clusterNumber] = true;
}
}
}
if (maxDefined[clusterNumber]) { // skip if seed for the cluster is not defined
/* Grow cluster around maximum, find centroid */
Index = (imax2[clusterNumber][1] + halfSize) * size + imax2[clusterNumber][0] + halfSize;
pixelList.clear();
pixelList.add(Index);
clusterMap[Index] = clusterNumber + 1;
listIndex = 0;
while (listIndex < pixelList.size()) {
Index = pixelList.get(listIndex++);
for (j = 0; j < dirs.length; j++) {
NewIndex = Index + dirs[j];
if ((NewIndex >= 0) && (NewIndex < clusterMap.length) && (clusterMap[NewIndex] == 0)
&& (pixels[NewIndex] < pixels[Index])) {
/* did we get too far? */
y = (NewIndex / size) - halfSize - imax2[clusterNumber][1];
x = (NewIndex % size) - halfSize - imax2[clusterNumber][0];
// System.out.println(" dy="+y+" dx="+x+" dx*dx+dy*dy="+(x*x+y*y));
if ((x * x + y * y) <= maxX2Y2) {
/*
* See if there is any neighbor of the new pixel that is higher and not yet
* marked (prevent rivers flowing between hills)
*/
noHills = true;
for (k = 0; k < dirs.length; k++) {
HillIndex = NewIndex + dirs[k];
if ((HillIndex >= 0) && (HillIndex < clusterMap.length)
&& (clusterMap[HillIndex] != (clusterNumber + 1))
&& (pixels[HillIndex] > pixels[NewIndex])) {
noHills = false;
break;
}
}
if (noHills) {
pixelList.add(NewIndex);
clusterMap[NewIndex] = clusterNumber + 1;
// System.out.println("NewIndex="+NewIndex+" y="+(NewIndex/size - halfSize)+"
// x="+((NewIndex % size) - halfSize)+" new pixel="+pixels[NewIndex]+" old
// pixel="+pixels[Index]);
}
}
}
}
}
/* Shrink clusters to a fraction of initial size */
// TODO: shring to a value (between min and max) if there is a sharp maximum??
// , double patternDetectParameters.shrinkClusters
if (patternDetectParameters.shrinkClusters == 0.0) { // use "smart" size
clusterSize = (int) Math.sqrt(5 * pixelList.size()); // use proportional size
} else if (patternDetectParameters.shrinkClusters < 0) {
clusterSize = (int) (-patternDetectParameters.shrinkClusters); // use specified size
} else {
clusterSize = (int) (pixelList.size() * patternDetectParameters.shrinkClusters); // use proportional
// size
}
if (clusterSize < 5)
clusterSize = 5;
while (pixelList.size() > clusterSize) {
i = 0;
f = pixels[pixelList.get(i)];
for (j = 1; j < pixelList.size(); j++)
if (pixels[pixelList.get(j)] < f) {
i = j;
f = pixels[pixelList.get(j)];
}
clusterMap[pixelList.get(i)] = -1; // Do not use looking for the next cluster
pixelList.remove(i);
}
/* now find centroid of the cluster */
minInCluster = pixels[pixelList.get(0)];
for (i = 1; i < pixelList.size(); i++)
if (minInCluster > pixels[pixelList.get(i)])
minInCluster = pixels[pixelList.get(i)];
cx = 0.0;
cy = 0.0;
cm = 0.0;
for (i = 0; i < pixelList.size(); i++) {
j = pixelList.get(i);
y = j / size - halfSize;
x = j % size - halfSize;
f = pixels[j] - minInCluster;
cm += f;
cx += f * x;
cy += f * y;
}
cx /= cm;
cy /= cm;
max2[clusterNumber][0] = cx;
max2[clusterNumber][1] = cy;
f = 0.0;
if (pair > 1) {
cx = max2[clusterNumber - 2][0] * pair / (pair - 1) - max2[clusterNumber][0];
cy = max2[clusterNumber - 2][1] * pair / (pair - 1) - max2[clusterNumber][1];
f = Math.sqrt(cx * cx + cy * cy);
/* Verify deviation here */
if (f > patternDetectParameters.deviation)
maxDefined[clusterNumber] = false;
}
if (this.debugLevel > 6)
System.out.println("pixelList.size()=" + pixelList.size() + " centroid sum=" + cm);
if (this.debugLevel > 5)
System.out.println("clusterNumber=" + clusterNumber + " x=" + max2[clusterNumber][0] + " y="
+ max2[clusterNumber][1] + " x0=" + (max2[clusterNumber][0] / pair) + " y0="
+ (max2[clusterNumber][1] / pair) + " deviat=" + f);
if ((cm == 0.0) || (pixelList.size() < 3))
maxDefined[clusterNumber] = false;
/* Filter out unreasonably low frequencies */
if ((max2[clusterNumber][0] * max2[clusterNumber][0]
+ max2[clusterNumber][1] * max2[clusterNumber][1]) < (reasonbleFrequency
* reasonbleFrequency)) {
if (this.debugLevel > 2)
System.out.println("Frequency too low:clusterNumber=" + clusterNumber + " x="
+ max2[clusterNumber][0] + " y=" + max2[clusterNumber][1]
+ ", minimal allowed frequency is " + reasonbleFrequency);
maxDefined[clusterNumber] = false;
}
}
}
/* Average (or just use farthest?) multiple maximums */
if (this.debugLevel > 2) {
float[] dbg_pixels = new float[clusterMap.length];
for (j = 0; j < dbg_pixels.length; j++)
dbg_pixels[j] = clusterMap[j];
dbg_pixels[(size + 1) * size / 2] = -1; // mark center
ImageProcessor ip = new FloatProcessor(size, size);
ip.setPixels(dbg_pixels);
ip.resetMinAndMax();
ImagePlus imp = new ImagePlus("clusters", ip);
imp.show();
}
// for (i=0;i<2;i++) for (j=0;j<2;j++) max2[i][j]=imax2[i][j];
double[][] maxFinal = new double[2][2];
boolean[] definedFinal = { false, false };
for (i = 0; i < 2; i++) {
maxFinal[i][0] = 0.0;
maxFinal[i][1] = 0.0;
j = 0;
for (pair = 0; pair < nmax / 2; pair++) {
if (maxDefined[i + 2 * pair]) {
j++;
maxFinal[i][0] += max2[i + 2 * pair][0] / (pair + 1);
maxFinal[i][1] += max2[i + 2 * pair][1] / (pair + 1);
definedFinal[i] = true;
// System.out.println("i="+i+" pair="+pair+" j="+j+"
// maxFinal["+i+"][0]="+maxFinal[i][0]+" maxFinal["+i+"][1]="+maxFinal[i][1]);
}
}
maxFinal[i][0] /= j;
maxFinal[i][1] /= j;
if (j == 0)
definedFinal[i] = false;
/*
* These two vectors correspond to checker diagonals and they are calculated for
* the correlation space. Actual frequencies for the checker board will be 1/4
* of these, also divide by FFT size so the result will be in cycles per pixel
*/
maxFinal[i][0] /= size * 4; // FIXME 4 - oversampling? Should it be here? Variable?
maxFinal[i][1] /= size * 4; // FIXME 4 - oversampling? Should it be here? Variable?
}
if (this.debugLevel > 2) {
System.out.println(
"Checkerboard frequency[0] x=" + IJ.d2s(maxFinal[0][0], 4) + " y=" + IJ.d2s(maxFinal[0][1], 4));
System.out.println(
"Checkerboard frequency[1] x=" + IJ.d2s(maxFinal[1][0], 4) + " y=" + IJ.d2s(maxFinal[1][1], 4));
// System.out.println();
}
if (!definedFinal[0] || !definedFinal[1]) {
if (this.debugLevel > 2) {
System.out.println("Undefined frequency(ies)");
}
return null;
}
return maxFinal;
}
/* ======================================================================== */
private double[] findCheckerPhases(double[][] WVectors, double[] P) {
double[][] DWVectors = { { WVectors[0][0] - WVectors[1][0], WVectors[0][1] - WVectors[1][1] },
{ WVectors[0][0] + WVectors[1][0], WVectors[0][1] + WVectors[1][1] } };
if (this.debugLevel > 3)
System.out.println(" DWVectors[0][0]=" + IJ.d2s(DWVectors[0][0], 4) + " DWVectors[0][1]="
+ IJ.d2s(DWVectors[0][1], 4));
if (this.debugLevel > 3)
System.out.println(" DWVectors[1][0]=" + IJ.d2s(DWVectors[1][0], 4) + " DWVectors[1][1]="
+ IJ.d2s(DWVectors[1][1], 4));
double[] DWVectorsAbs2 = { DWVectors[0][0] * DWVectors[0][0] + DWVectors[0][1] * DWVectors[0][1],
DWVectors[1][0] * DWVectors[1][0] + DWVectors[1][1] * DWVectors[1][1] };
if (this.debugLevel > 3)
System.out.println(" sqrt(DWVectorsAbs2[0])=" + IJ.d2s(Math.sqrt(DWVectorsAbs2[0]), 4)
+ " sqrt(DWVectorsAbs2[1])=" + IJ.d2s(Math.sqrt(DWVectorsAbs2[1]), 4));
double[][] DL = { { DWVectors[0][0] / DWVectorsAbs2[0], DWVectors[0][1] / DWVectorsAbs2[0] },
{ DWVectors[1][0] / DWVectorsAbs2[1], DWVectors[1][1] / DWVectorsAbs2[1] } };
if (this.debugLevel > 3)
System.out.println(" DL[0][0]=" + IJ.d2s(DL[0][0], 4) + " DL[0][1]=" + IJ.d2s(DL[0][1], 4));
if (this.debugLevel > 3)
System.out.println(" DL[1][0]=" + IJ.d2s(DL[1][0], 4) + " DL[1][1]=" + IJ.d2s(DL[1][1], 4));
double v = (DL[0][0] * (DL[1][0] * P[1] - DL[0][0] * P[0]) + DL[0][1] * (DL[1][1] * P[1] - DL[0][1] * P[0]))
/ (DL[0][0] * DL[1][1] - DL[0][1] * DL[1][0]) / (2 * Math.PI);
if (this.debugLevel > 2)
System.out.println("v=" + IJ.d2s(v, 4));
double[] WC = { -DL[1][0] * P[1] / (2 * Math.PI) + DL[1][1] * v,
-DL[1][1] * P[1] / (2 * Math.PI) - DL[1][0] * v };
if (this.debugLevel > 2)
System.out.println("WC[0]=" + IJ.d2s(WC[0], 4) + " WC[1]=" + IJ.d2s(WC[1], 4));
double[] phases = { -2 * Math.PI * (WC[0] * WVectors[0][0] + WC[1] * WVectors[0][1]),
-2 * Math.PI * (WC[0] * WVectors[1][0] + WC[1] * WVectors[1][1]) };
if (this.debugLevel > 2)
System.out.println("phases[0]=" + IJ.d2s(phases[0], 4) + " phases[1]=" + IJ.d2s(phases[1], 4));
return phases;
}
/* ======================================================================== */
private boolean matchPatterns(double[][][] patterns, double[][] targetPattern) {
int n, i, j;
double[][] sp = new double[2][2];
double[] target_lengths = new double[2];
double[] current_lengths = new double[2];
double[] swap_wv;
double swap;
boolean noCorrectionWasNeeded = true;
if (patterns == null)
return false;
if (targetPattern == null)
return false;
for (n = 0; n < patterns.length; n++) {
for (i = 0; i < 2; i++) {
target_lengths[i] = Math
.sqrt(targetPattern[i][0] * targetPattern[i][0] + targetPattern[i][1] * targetPattern[i][1]);
current_lengths[i] = Math
.sqrt(patterns[n][i][0] * patterns[n][i][0] + patterns[n][i][1] * patterns[n][i][1]);
}
for (i = 0; i < 2; i++)
for (j = 0; j < 2; j++) {
sp[i][j] = (patterns[n][i][0] * targetPattern[j][0] + patterns[n][i][1] * targetPattern[j][1])
/ current_lengths[i] / target_lengths[j];
}
/* Swap vectors regardless of sign (parallel/anti-parallel) */
if ((Math.abs(sp[0][0]) < Math.abs(sp[0][1])) || (Math.abs(sp[0][0]) < Math.abs(sp[1][0]))) {
noCorrectionWasNeeded = false;
if (this.debugLevel > 0)
System.out.println("Swapped wave vectors in quadrant " + n);
swap_wv = patterns[n][0];
patterns[n][0] = patterns[n][1];
patterns[n][1] = swap_wv;
swap = sp[0][0];
sp[0][0] = sp[1][0];
sp[1][0] = swap;
swap = sp[0][1];
sp[0][1] = sp[1][1];
sp[1][1] = swap;
}
/* Now correct vector signs if needed */
for (i = 0; i < 2; i++) {
if (sp[i][i] < 0) {
noCorrectionWasNeeded = false;
if (this.debugLevel > 0)
System.out.println("Changing wave vector " + (i + 1) + " direction in quadrant " + n);
for (j = 0; j < patterns[n][i].length; j++)
patterns[n][i][j] = -patterns[n][i][j]; /// Will negate phase if available
}
}
}
return noCorrectionWasNeeded;
}
/*
* ** converts 2 wave vectors (WVx,WVy,phase) into two checker pattern vectors
* (VPx,VPy, phase) phase is in the point x=0,y=0
*/
private double[][] waveVectorsToPatternVectors(double[] wv0, double[] wv1) {
double[][] v = new double[2][3];
double vect_wv0_x_wv1 = wv0[0] * wv1[1] - wv0[1] * wv1[0];
// v[0][0]= wv0[1]/vect_wv0_x_wv1;
// v[0][1]=-wv0[0]/vect_wv0_x_wv1;
// v[1][0]=-wv1[1]/vect_wv0_x_wv1;
// v[1][1]= wv1[0]/vect_wv0_x_wv1;
v[0][0] = wv1[1] / vect_wv0_x_wv1;
v[0][1] = -wv1[0] / vect_wv0_x_wv1;
v[1][0] = -wv0[1] / vect_wv0_x_wv1;
v[1][1] = wv0[0] / vect_wv0_x_wv1;
if (wv0.length > 2)
v[0][2] = wv0[2];
if (wv1.length > 2)
v[1][2] = wv1[2];
/*
* "white square" center had coordinates -wv0[0]
*/
return v;
}
/**
* Matching non-linear mesh using wave vectors in the centers of four quadrants,
* phases are ignored here processes wave vectors for the four quadrants
* (top-left,tr,bl,br) and calculates second degree polynominal correction X1=
* Ax*X^2 + Bx*Y^2 + 2Cx*X*Y Y1= Ay*X^2 + By*Y^2 + 2Cy*X*Y where X and Y are
* normalized so top left corner is (-1,-1), top right - (+1,-1) , bottom left -
* (-1,+1), bottom right - (+1,+1) returns array of 6 elements
* {Ax,Bx,Cx,Ay,By,Cy}
*/
private double[] calcPatternNonLinear(double[][][] qp) {
int iq;
/* Calculate center WV */
double[][][] mCorners = new double[4][][]; // only two 2x2 are needed, other two - just for debugging
mCorners[0] = matrixToConvertTwoPairs(qp[0][0], qp[0][1], qp[4][0], qp[4][1]);
mCorners[1] = matrixToConvertTwoPairs(qp[1][0], qp[1][1], qp[4][0], qp[4][1]);
mCorners[2] = matrixToConvertTwoPairs(qp[2][0], qp[2][1], qp[4][0], qp[4][1]);
mCorners[3] = matrixToConvertTwoPairs(qp[3][0], qp[3][1], qp[4][0], qp[4][1]);
/**
* x,y - image coordinates (distorted), with no center shift (later use
* effective radius) x1,y1 - linear mesh
*
* x1=x+Ax*x^2+Bx*y^2+2*Cx*x*y+Dx*x+Ex*y y1=y+Ay*x^2+By*y^2+2*Cy*x*y+Dy*x+Ey*y
*
* dx1/dx=1+2*Ax*x+2*Cx*y+Dx dx1/dy=0+2*Cx*x+2*Bx*y+Ex
*
* dy1/dx=0+2*Ay*x+2*Cy*y+Dy dy1/dy=1+2*Cy*x+2*By*y+Ey
*
* | dx1 | = | 1+2*Ax*x+2*Cx*y+Dx 2*Cx*x+2*Bx*y+Ex | * | dx | | dy1 | |
* 2*Ay*x+2*Cy*y+Dy 1+2*Cy*x+2*By*y+Ey | | dy |
*
* for x=-1,y=-1 M[0]=| 1-2*Ax-2*Cx+Dx -2*Cx-2*Bx+Ex | | -2*Ay-2*Cy+Dy
* 1-2*Cy-2*By+Ey |
*
* for x=+1,y=-1 M[1]=| 1+2*Ax-2*Cx+Dx +2*Cx-2*Bx+Ex | | +2*Ay-2*Cy+Dy
* 1+2*Cy-2*By+Ey |
*
* for x=-1,y=+1 M[2]=| 1-2*Ax+2*Cx+Dx -2*Cx+2*Bx+Ex | | -2*Ay+2*Cy+Dy
* 1-2*Cy+2*By+Ey |
*
* for x=+1,y=+1 M[2]=| 1+2*Ax+2*Cx+Dx +2*Cx+2*Bx+Ex | | +2*Ay+2*Cy+Dy
* 1+2*Cy+2*By+Ey |
*
*
*/
double[] det = { mCorners[0][0][0] * mCorners[0][1][1] - mCorners[0][0][1] * mCorners[0][1][0],
mCorners[1][0][0] * mCorners[1][1][1] - mCorners[1][0][1] * mCorners[1][1][0],
mCorners[2][0][0] * mCorners[2][1][1] - mCorners[2][0][1] * mCorners[2][1][0],
mCorners[3][0][0] * mCorners[3][1][1] - mCorners[3][0][1] * mCorners[3][1][0] };
double[][][] M = {
{ { mCorners[0][1][1] / det[0], -mCorners[0][1][0] / det[0] },
{ -mCorners[0][0][1] / det[0], mCorners[0][0][0] / det[0] } },
{ { mCorners[1][1][1] / det[1], -mCorners[1][1][0] / det[1] },
{ -mCorners[1][0][1] / det[1], mCorners[1][0][0] / det[1] } },
{ { mCorners[2][1][1] / det[2], -mCorners[2][1][0] / det[2] },
{ -mCorners[2][0][1] / det[2], mCorners[2][0][0] / det[2] } },
{ { mCorners[3][1][1] / det[3], -mCorners[3][1][0] / det[3] },
{ -mCorners[3][0][1] / det[3], mCorners[3][0][0] / det[3] } } };
/**
* Overdefined - 16 equations for 10 unknowns 1 M[0][0][0]=1-2*Ax-2*Cx+Dx 2
* M[0][0][1]= -2*Cx-2*Bx+Ex 3 M[0][1][0]= -2*Ay-2*Cy+Dy 4
* M[0][1][1]=1-2*Cy-2*By+Ey 5 M[1][0][0]=1+2*Ax-2*Cx+Dx 6 M[1][0][1]=
* 2*Cx-2*Bx+Ex 7 M[1][1][0]= 2*Ay-2*Cy+Dy 8 M[1][1][1]=1+2*Cy-2*By+Ey
*
* 9 M[2][0][0]=1-2*Ax+2*Cx+Dx 10 M[2][0][1]= -2*Cx+2*Bx+Ex 11 M[2][1][0]=
* -2*Ay+2*Cy+Dy 12 M[2][1][1]=1-2*Cy+2*By+Ey 13 M[3][0][0]=1+2*Ax+2*Cx+Dx 14
* M[3][0][1]= 2*Cx+2*Bx+Ex 15 M[3][1][0]= 2*Ay+2*Cy+Dy 16
* M[3][1][1]=1+2*Cy+2*By+Ey
*
* ( 1) M[0][0][0]=1-2*Ax -2*Cx +Dx ( 2) M[0][0][1]= -2*Bx-2*Cx +Ex ( 3)
* M[0][1][0]= -2*Ay -2*Cy +Dy ( 4) M[0][1][1]=1 -2*By-2*Cy +Ey ( 5)
* M[1][0][0]=1+2*Ax -2*Cx +Dx ( 6) M[1][0][1]= -2*Bx+2*Cx +Ex ( 7) M[1][1][0]=
* +2*Ay -2*Cy +Dy ( 8) M[1][1][1]=1 -2*By+2*Cy +Ey
*
* ( 9) M[0][0][0]=1-2*Ax +2*Cx +Dx (10) M[0][0][1]= +2*Bx-2*Cx +Ex (11)
* M[0][1][0]= -2*Ay +2*Cy +Dy (12) M[0][1][1]=1 +2*By-2*Cy +Ey (13)
* M[1][0][0]=1+2*Ax +2*Cx +Dx (14) M[1][0][1]= +2*Bx+2*Cx +Ex (15) M[1][1][0]=
* +2*Ay +2*Cy +Dy (16) M[1][1][1]=1 +2*By+2*Cy +Ey
*
* ( 1)+( 5)+( 9)+(13) Dx=( M[0][0][0]+M[1][0][0]+M[2][0][0]+M[3][0][0])/4-1 (
* 2)+( 6)+(10)+(14) Ex=( M[0][0][1]+M[1][0][1]+M[2][0][1]+M[3][0][1])/4 ( 3)+(
* 7)+(11)+(15) Dy=( M[0][1][0]+M[1][1][0]+M[2][1][0]+M[3][1][0])/4 ( 4)+(
* 8)+(12)+(16) Ey=( M[0][1][1]+M[1][1][1]+M[2][1][1]+M[3][1][1])/4-1
*
* -( 1)+( 5)-( 9)+(13) Ax=(-M[0][0][0]+M[1][0][0]-M[2][0][0]+M[3][0][0])/8 -(
* 2)-( 6)+(10)+(14) Bx=(-M[0][0][1]-M[1][0][1]+M[2][0][1]+M[3][0][1])/8 -( 2)+(
* 6)-(10)+(14)-( 1)-( 5)+( 9)+(13)
* Cx=(-M[0][0][1]+M[1][0][1]-M[2][0][1]+M[3][0][1]-M[0][0][0]-M[1][0][0]+M[2][0][0]+M[3][0][0])/16
* -( 3)+( 7)-(11)+(15) Ay=(-M[0][1][0]+M[1][1][0]-M[2][1][0]+M[3][1][0])/8 -(
* 4)-( 8)+(12)+(16) By=(-M[0][1][1]-M[1][1][1]+M[2][1][1]+M[3][1][1])/8 -( 4)+(
* 8)-(12)+(16)-( 3)-( 7)+(11)+(15)
* Cy=(-M[0][1][1]+M[1][1][1]-M[2][1][1]+M[3][1][1]-M[0][1][0]-M[1][1][0]+M[2][1][0]+M[3][1][0])/16
*/
double[] rslt = { (-M[0][0][0] + M[1][0][0] - M[2][0][0] + M[3][0][0]) / 8, // Ax
(-M[0][0][1] - M[1][0][1] + M[2][0][1] + M[3][0][1]) / 8, // Bx
(-M[0][0][1] + M[1][0][1] - M[2][0][1] + M[3][0][1] - M[0][0][0] - M[1][0][0] + M[2][0][0] + M[3][0][0])
/ 16, // Cx
(-M[0][1][0] + M[1][1][0] - M[2][1][0] + M[3][1][0]) / 8, // Ay
(-M[0][1][1] - M[1][1][1] + M[2][1][1] + M[3][1][1]) / 8, // By
(-M[0][1][1] + M[1][1][1] - M[2][1][1] + M[3][1][1] - M[0][1][0] - M[1][1][0] + M[2][1][0] + M[3][1][0])
/ 16, // Cy
(M[0][0][0] + M[1][0][0] + M[2][0][0] + M[3][0][0]) / 4 - 1, // Dx
(M[0][0][1] + M[1][0][1] + M[2][0][1] + M[3][0][1]) / 4, // Ex
(M[0][1][0] + M[1][1][0] + M[2][1][0] + M[3][1][0]) / 4, // Dy
(M[0][1][1] + M[1][1][1] + M[2][1][1] + M[3][1][1]) / 4 - 1 // Ey
};
if (this.debugLevel > 2) { /* increase LEVEL later */
/*
* System.out.println("Center "+ " W0x="+ IJ.d2s(centerWV[0][0],4)+ " W0y="+
* IJ.d2s(centerWV[0][1],4)+ " W1x="+ IJ.d2s(centerWV[1][0],4)+ " W1y="+
* IJ.d2s(centerWV[1][1],4));
*/
for (iq = 0; iq < 4; iq++) {
System.out.println("Matrix= " + iq + " M00=" + IJ.d2s(mCorners[iq][0][0], 4) + " M01="
+ IJ.d2s(mCorners[iq][0][1], 4) + " M10=" + IJ.d2s(mCorners[iq][1][0], 4) + " M11="
+ IJ.d2s(mCorners[iq][1][1], 4));
}
for (iq = 0; iq < 4; iq++) {
System.out.println(" M[" + iq + "][0][0]=" + IJ.d2s(M[iq][0][0], 4) + " M[" + iq + "][0][1]="
+ IJ.d2s(M[iq][0][1], 4) + " M[" + iq + "][1][0]=" + IJ.d2s(M[iq][1][0], 4) + " M[" + iq
+ "][1][1]=" + IJ.d2s(M[iq][1][1], 4));
}
}
if (this.debugLevel > 2) { /* increase LEVEL later */
System.out.println("Corr " + " Ax=" + IJ.d2s(rslt[0], 5) + " Bx=" + IJ.d2s(rslt[1], 5) + " Cx="
+ IJ.d2s(rslt[2], 5) + " Ay=" + IJ.d2s(rslt[3], 5) + " By=" + IJ.d2s(rslt[4], 5) + " Cy="
+ IJ.d2s(rslt[5], 5) + " Dx=" + IJ.d2s(rslt[6], 5) + " Ex=" + IJ.d2s(rslt[7], 5) + " Dy="
+ IJ.d2s(rslt[8], 5) + " Ey=" + IJ.d2s(rslt[9], 5));
}
return rslt;
};
/* ======================================================================== */
/* TODO: REPLACE doubleFHT */
/*
* converts FHT results (frequency space) to complex numbers of
* [fftsize/2+1][fftsize]
*/
private double[][][] FHT2FFTHalf(FHT fht, int fftsize) {
float[] fht_pixels = (float[]) fht.getPixels();
double[][][] fftHalf = new double[(fftsize >> 1) + 1][fftsize][2];
int row1, row2, col1, col2;
for (row1 = 0; row1 <= (fftsize >> 1); row1++) {
row2 = (fftsize - row1) % fftsize;
for (col1 = 0; col1 < fftsize; col1++) {
col2 = (fftsize - col1) % fftsize;
fftHalf[row1][col1][0] = 0.5 * (fht_pixels[row1 * fftsize + col1] + fht_pixels[row2 * fftsize + col2]);
fftHalf[row1][col1][1] = 0.5 * (fht_pixels[row2 * fftsize + col2] - fht_pixels[row1 * fftsize + col1]);
}
}
return fftHalf;
}
/* ======================================================================== */
public double[][][] FHT2FFTHalf(double[] fht_pixels, int fftsize) {
double[][][] fftHalf = new double[(fftsize >> 1) + 1][fftsize][2];
int row1, row2, col1, col2;
for (row1 = 0; row1 <= (fftsize >> 1); row1++) {
row2 = (fftsize - row1) % fftsize;
for (col1 = 0; col1 < fftsize; col1++) {
col2 = (fftsize - col1) % fftsize;
fftHalf[row1][col1][0] = 0.5 * (fht_pixels[row1 * fftsize + col1] + fht_pixels[row2 * fftsize + col2]);
fftHalf[row1][col1][1] = 0.5 * (fht_pixels[row2 * fftsize + col2] - fht_pixels[row1 * fftsize + col1]);
}
}
return fftHalf;
}
/* ======================================================================== */
/*
* converts FFT arrays of complex numbers of [fftsize/2+1][fftsize] to FHT
* arrays
*/
private float[] floatFFTHalf2FHT(double[][][] fft, int fftsize) {
float[] fht_pixels = new float[fftsize * fftsize];
int row1, row2, col1, col2;
for (row1 = 0; row1 <= (fftsize >> 1); row1++) {
row2 = (fftsize - row1) % fftsize;
for (col1 = 0; col1 < fftsize; col1++) {
col2 = (fftsize - col1) % fftsize;
fht_pixels[row1 * fftsize + col1] = (float) (fft[row1][col1][0] - fft[row1][col1][1]);
fht_pixels[row2 * fftsize + col2] = (float) (fft[row1][col1][0] + fft[row1][col1][1]);
}
}
return fht_pixels;
}
/* ======================================================================== */
public double[][] normalizeAndWindow(double[][] pixels, double[] windowFunction) {
return normalizeAndWindow(pixels, windowFunction, true);
}
private double[] normalizeAndWindow(double[] pixels, double[] windowFunction) {
return normalizeAndWindow(pixels, windowFunction, true);
}
private double[][] normalizeAndWindow(double[][] pixels, double[] windowFunction, boolean removeDC) {
int i;
for (i = 0; i < pixels.length; i++)
if (pixels[i] != null)
pixels[i] = normalizeAndWindow(pixels[i], windowFunction, removeDC);
return pixels;
}
private double[] normalizeAndWindow(double[] pixels, double[] windowFunction, boolean removeDC) {
int j;
if (pixels == null)
return null;
double s = 0.0, s0 = 0.0;
if (removeDC) {
for (j = 0; j < pixels.length; j++) {
s += pixels[j] * windowFunction[j];
s0 += windowFunction[j];
}
s /= s0;
}
for (j = 0; j < pixels.length; j++)
pixels[j] = (pixels[j] - s) * windowFunction[j];
return pixels;
}
/* ======================================================================== */
public double[][] matrix2x2_invert(double[][] m) {
double det = m[0][0] * m[1][1] - m[0][1] * m[1][0];
double[][] rslt = { { m[1][1] / det, -m[0][1] / det }, { -m[1][0] / det, m[0][0] / det } };
return rslt;
}
public double[][] matrix2x2_mul(double[][] a, double[][] b) {
double[][] rslt = { { a[0][0] * b[0][0] + a[0][1] * b[1][0], a[0][0] * b[0][1] + a[0][1] * b[1][1] },
{ a[1][0] * b[0][0] + a[1][1] * b[1][0], a[1][0] * b[0][1] + a[1][1] * b[1][1] } };
return rslt;
}
public double[] matrix2x2_mul(double[][] a, double[] b) {
double[] rslt = { a[0][0] * b[0] + a[0][1] * b[1], a[1][0] * b[0] + a[1][1] * b[1] };
return rslt;
}
public double[][] matrix2x2_scale(double[][] a, double b) {
double[][] rslt = { { a[0][0] * b, a[0][1] * b }, { a[1][0] * b, a[1][1] * b } };
return rslt;
}
public double[] matrix2x2_scale(double[] a, double b) {
double[] rslt = { a[0] * b, a[1] * b };
return rslt;
}
public double[][] matrix_add(double[][] a, double[][] b) {
double[][] rslt = new double[a.length][a[0].length];
int i, j;
for (i = 0; i < rslt.length; i++)
for (j = 0; j < rslt[0].length; j++)
rslt[i][j] = a[i][j] + b[i][j];
return rslt;
}
public double[] vector_add(double[] a, double[] b) {
double[] rslt = new double[a.length];
int i;
for (i = 0; i < rslt.length; i++)
rslt[i] = a[i] + b[i];
return rslt;
}
/* calculates 2x2 matrix that converts two pairs of vectors: u2=M*u1, v2=M*v1 */
private double[][] matrixToConvertTwoPairs(double[] u1, double[] v1, double[] u2, double[] v2) {
double[][] rslt = {
{ (u2[0] * v1[1] - v2[0] * u1[1]) / (u1[0] * v1[1] - v1[0] * u1[1]),
(v2[0] * u1[0] - u2[0] * v1[0]) / (u1[0] * v1[1] - v1[0] * u1[1]) },
{ (u2[1] * v1[1] - v2[1] * u1[1]) / (u1[0] * v1[1] - v1[0] * u1[1]),
(v2[1] * u1[0] - u2[1] * v1[0]) / (u1[0] * v1[1] - v1[0] * u1[1]) } };
return rslt;
}
public double[][] matrix2x2_add(double[][] a, double[][] b) {
double[][] rslt = { { a[0][0] + b[0][0], a[0][1] + b[0][1] }, { a[1][0] + b[1][0], a[1][1] + b[1][1] } };
return rslt;
}
public double[] matrix2x2_add(double[] a, double[] b) {
double[] rslt = { a[0] + b[0], a[1] + b[1] };
return rslt;
}
public int[][] matrix2x2_add(int[][] a, int[][] b) {
int[][] rslt = { { a[0][0] + b[0][0], a[0][1] + b[0][1] }, { a[1][0] + b[1][0], a[1][1] + b[1][1] } };
return rslt;
}
public int[] matrix2x2_add(int[] a, int[] b) {
int[] rslt = { a[0] + b[0], a[1] + b[1] };
return rslt;
}
public double[][] matrix2x2_transp(double[][] m) {
double[][] rslt = { { m[0][0], m[1][0] }, { m[0][1], m[1][1] } };
return rslt;
}
/* ======================================================================== */
private static double[] combineDiagonalGreens(double[] green0, double[] green3, int half_width, int half_height) {
int y, x, base;
int base_b = 0;
double[] result = new double[green0.length];
for (y = 0; y < half_height / 2; y++) {
base = half_height * half_width / 2 + y * (half_width + 1);
for (x = 0; x < half_width / 2; x++) {
result[base_b++] = green0[base];
base -= half_width;
result[base_b++] = green3[base++];
}
base = half_height * half_width / 2 + y * (half_width + 1);
for (x = 0; x < half_width / 2; x++) {
// System.out.println("2:y="+y+" x="+x+" base_b="+base_b+" base="+base);
result[base_b++] = green3[base++];
result[base_b++] = green0[base];
base -= half_width;
}
}
return result;
}
/* ======================================================================== */
public double[] initWindowFunction(int size, double gaussWidth) {
return initWindowFunction(size, gaussWidth, 0);
}
public double[] initWindowFunction(int size, double gaussWidth, int zeros) {
double[] windowFunction = new double[size * size];
double[] windowFunction_line = new double[size];
double a, k;
int i, j;
int size1 = size - zeros;
int i0 = (zeros + 1) / 2;
if (gaussWidth == 0) {
for (i = 0; i < size; i++)
windowFunction_line[i] = 1.0;
} else if (gaussWidth < 0) {
for (i = 0; i < size1; i++)
windowFunction_line[i + i0] = (0.54 - 0.46 * Math.cos((i * 2.0 * Math.PI) / size1));
} else {
k = 2.0 / (size * gaussWidth);
for (i = i0; i < i0 + size1; i++) {
a = (i - size / 2) * k;
windowFunction_line[i] = Math.exp(-a * a);
if (zeros > 0)
windowFunction_line[i] *= (0.54 - 0.46 * Math.cos(((i - i0) * 2.0 * Math.PI) / size1)); // additionally
// multiply
// by
// Hamming
}
}
if (zeros > 0) { // make window to be exact zero for certain number of samples (for correlation)
for (i = 0; i < i0; i++)
windowFunction_line[i] = 0.0;
for (i = i0 + size1; i < size; i++)
windowFunction_line[i] = 0.0;
}
for (i = 0; i < size; i++)
for (j = 0; j < size; j++) {
windowFunction[size * i + j] = windowFunction_line[i] * windowFunction_line[j];
}
return windowFunction;
}
/*
* ============================= Distortions ===================================
*/
public void distortionsTest(
final DistortionParameters distortionParameters, //
final MatchSimulatedPattern.PatternDetectParameters patternDetectParameters,
final SimulationPattern.SimulParameters simulParameters,
final boolean equalizeGreens,
final ImagePlus imp, // image to process
final int threadsMax,
final boolean updateStatus,
final int debug_level) {// debug level used inside loops
if (imp == null)
return;
final int sensor_type = LwirReaderParameters.sensorType(imp);
Roi roi = imp.getRoi();
final Rectangle selection;
if (roi == null) {
selection = new Rectangle(0, 0, imp.getWidth(), imp.getHeight());
} else {
selection = (roi instanceof PointRoi) ? (new Rectangle(0, 0, imp.getWidth(), imp.getHeight()))
: roi.getBounds();
}
MatchSimulatedPattern matchSimulatedPattern = new MatchSimulatedPattern(distortionParameters.getFFTSize(sensor_type));
matchSimulatedPattern.debugLevel = debugLevel;
MatchSimulatedPattern matchSimulatedPatternCorr = new MatchSimulatedPattern(
distortionParameters.getCorrelationSize(sensor_type));
matchSimulatedPatternCorr.debugLevel = debugLevel;
final SimulationPattern.SimulParameters thisSimulParameters = simulParameters.clone();
thisSimulParameters.subdiv = distortionParameters.patternSubdiv;
thisSimulParameters.bPatternSigma = distortionParameters.bPatternSigma;
thisSimulParameters.barraySigma = distortionParameters.barraySigma;
SimulationPattern simulationPattern = new SimulationPattern(thisSimulParameters);
final double[] bPattern = simulationPattern.patternGenerator(simulParameters); // reuse pattern for next time
// find center of the selection (to be used to find initial pattern
// approximation)
if (debugLevel > 2)
System.out.println("bPattern.length=" + bPattern.length);
int xc, yc;
xc = 2 * ((2 * selection.x + selection.width + 1) / 4);
yc = 2 * ((2 * selection.y + selection.height + 1) / 4);
Rectangle initialPatternCell = new Rectangle(xc - distortionParameters.FFTSize,
yc - distortionParameters.FFTSize, 2 * distortionParameters.FFTSize, 2 * distortionParameters.FFTSize);
// create diagonal green selection around xc,yc
double[][] input_bayer = splitBayer(imp, initialPatternCell, equalizeGreens);
if (debugLevel > 2)
ShowDoubleFloatArrays.showArrays(input_bayer, true, "selection-bayer-distortionsTest");
double[] windowFunction = initWindowFunction(distortionParameters.FFTSize, distortionParameters.fftGaussWidth);
final double[] windowFunctionCorr = initWindowFunction(distortionParameters.getCorrelationSize(sensor_type),
distortionParameters.correlationGaussWidth, distortionParameters.zeros);
double[] greens = normalizeAndWindow(input_bayer[4], windowFunction);
double[][] pattern = matchSimulatedPattern.findPattern(null, // DoubleFHT doubleFHT,
greens, distortionParameters.getFFTSize(sensor_type), patternDetectParameters,
patternDetectParameters.minGridPeriod / 2, patternDetectParameters.maxGridPeriod / 2, true, // this is a
// pattern
// for
// combined
// greens
// (diagonal),
// adjust
// results
// accordingly
"Pattern"); // title - will not be used
if (pattern == null) {
System.out.println("Error - pattern not found");
IJ.showMessage("Error", "Failed to find pattern");
return;
}
if (debugLevel > 2)
System.out.println("FX1=" + pattern[0][0] + " FY1=" + pattern[0][1] + " phase1=" + pattern[0][2]);
if (debugLevel > 2)
System.out.println("FX2=" + pattern[1][0] + " FY2=" + pattern[1][1] + " phase2=" + pattern[1][2]);
double[] ll2 = new double[2];
double[][] dxy = new double[2][2];
double[][] phases = new double[2][2];
int i, j, k;
for (i = 0; i < 2; i++) {
ll2[i] = (pattern[i][0] * pattern[i][0] + pattern[i][1] * pattern[i][1]) * 2 * Math.PI;
}
if (debugLevel > 2)
System.out.println(
"phase1/2pi=" + (pattern[0][2] / 2 / Math.PI) + " phase2/2pi=" + (pattern[1][2] / 2 / Math.PI));
for (k = 0; k < 2; k++) {
for (j = 0; j < 2; j++) {
phases[k][j] = pattern[j][2] - Math.PI / 2 + ((k > 0) ? Math.PI : 0.0);
while (phases[k][j] < -Math.PI)
phases[k][j] += 2 * Math.PI;
while (phases[k][j] > Math.PI)
phases[k][j] -= 2 * Math.PI;
}
if (debugLevel > 2)
System.out.println(
"phase1/2pi=" + (phases[k][0] / 2 / Math.PI) + " phase2/2pi=" + (phases[k][1] / 2 / Math.PI));
for (i = 0; i < 2; i++) {
dxy[k][i] = 0;
for (j = 0; j < 2; j++) {
dxy[k][i] += (phases[k][j]) * pattern[j][i] / ll2[j];
}
}
if (debugLevel > 2)
System.out.println("dX[" + k + "]=" + dxy[k][0] + " dY[" + k + "]=" + dxy[k][1]);
}
int phaseSel = ((dxy[0][0] * dxy[0][0] + dxy[0][1] * dxy[0][1]) < (dxy[1][0] * dxy[1][0]
+ dxy[1][1] * dxy[1][1])) ? 0 : 1;
if (debugLevel > 1)
System.out.println("xc=" + xc + " yc=" + yc);
if (debugLevel > 1)
System.out.println("dX=" + dxy[phaseSel][0] + " dY=" + dxy[phaseSel][1]);
double[] centerXY0 = { xc - dxy[phaseSel][0], yc - dxy[phaseSel][1] };
if (debugLevel > 1)
System.out.println("+++ Initial center x=" + IJ.d2s(centerXY0[0], 3) + " y=" + IJ.d2s(centerXY0[1], 3));
// debug mode - scan correlation around center point, show result and exit:
ShowDoubleFloatArrays.showArrays(simulationPattern.bPattern, "bPattern");
// double [] barray= new double
// [simulationPattern.barray.length*simulationPattern.barray[0].length];
// for (i=0;i (debug_threshold + 0)) {
double[][] dbg_img = { dbg_dpixels, dpixels };
ShowDoubleFloatArrays.showArrays(dbg_img, true,
"selection-input" + (initialPatternCell.x + initialPatternCell.width / 2) + ":"
+ (initialPatternCell.y + initialPatternCell.height / 2));
}
pattern = matchSimulatedPattern.findPattern(doubleFHT, dpixels, fft_size, patternDetectParameters,
min_half_period, max_half_period, false, // this is a pattern for combined greens (diagonal), adjust
// results accordingly
"Pattern"); // title - will not be used
} else { // Bayer - extract green
// create diagonal green selection around xc,yc
double[][] input_bayer = splitBayer(imp, initialPatternCell, equalizeGreens);
if (debugLevel > (debug_threshold + 0)) {
ShowDoubleFloatArrays.showArrays(input_bayer, true, "selection--bayer");
}
double[] greens = normalizeAndWindow(input_bayer[4], windowFunction);
pattern = matchSimulatedPattern.findPattern(doubleFHT, greens, fft_size, patternDetectParameters,
min_half_period, max_half_period, true, // this is a pattern for combined greens (diagonal), adjust
// results accordingly
"Pattern"); // title - will not be used
}
if (pattern == null) {
// System.out.println("Error - pattern not found");
// IJ.showMessage("Error","Failed to find pattern");
if (dbgStr != null)
System.out.println(dbgStr + " matchSimulatedPattern.findPattern->null");
return null;
}
if (debugLevel > (debug_threshold + 0))
System.out.println("FX1=" + pattern[0][0] + " FY1=" + pattern[0][1] + " phase1=" + pattern[0][2]);
if (debugLevel > (debug_threshold + 0))
System.out.println("FX2=" + pattern[1][0] + " FY2=" + pattern[1][1] + " phase2=" + pattern[1][2]);
double[] ll2 = new double[2];
double[][] dxy = new double[2][2];
double[][] phases = new double[2][2];
int i, j, k;
for (i = 0; i < 2; i++) {
ll2[i] = (pattern[i][0] * pattern[i][0] + pattern[i][1] * pattern[i][1]) * 2 * Math.PI;
}
if (debugLevel > (debug_threshold + 0))
System.out.println(
"phase1/2pi=" + (pattern[0][2] / 2 / Math.PI) + " phase2/2pi=" + (pattern[1][2] / 2 / Math.PI));
for (k = 0; k < 2; k++) {
for (j = 0; j < 2; j++) {
phases[k][j] = pattern[j][2] - Math.PI / 2 + ((k > 0) ? Math.PI : 0.0);
while (phases[k][j] < -Math.PI)
phases[k][j] += 2 * Math.PI;
while (phases[k][j] > Math.PI)
phases[k][j] -= 2 * Math.PI;
}
if (debugLevel > (debug_threshold + 0))
System.out.println(
"phase1/2pi=" + (phases[k][0] / 2 / Math.PI) + " phase2/2pi=" + (phases[k][1] / 2 / Math.PI));
for (i = 0; i < 2; i++) {
dxy[k][i] = 0;
for (j = 0; j < 2; j++) {
dxy[k][i] += (phases[k][j]) * pattern[j][i] / ll2[j];
}
}
if (debugLevel > (debug_threshold + 0))
System.out.println("dX[" + k + "]=" + dxy[k][0] + " dY[" + k + "]=" + dxy[k][1]);
}
int phaseSel = ((dxy[0][0] * dxy[0][0] + dxy[0][1] * dxy[0][1]) < (dxy[1][0] * dxy[1][0]
+ dxy[1][1] * dxy[1][1])) ? 0 : 1;
if (debugLevel > (debug_threshold + 0))
System.out.println("xc=" + xc + " yc=" + yc);
if (debugLevel > (debug_threshold + 0))
System.out.println("dX=" + dxy[phaseSel][0] + " dY=" + dxy[phaseSel][1]);
double[] centerXY0 = { xc - dxy[phaseSel][0], yc - dxy[phaseSel][1] };
if (debugLevel > (debug_threshold - 1))
System.out.println("+++ Initial center x=" + IJ.d2s(centerXY0[0], 3) + " y=" + IJ.d2s(centerXY0[1], 3));
/*
* if (debugLevel > (debug_threshold + 0)){ double [] test_error_offset = {0.0,
* 2.0}; centerXY0[0] += test_error_offset[0]; centerXY0[1] +=
* test_error_offset[1];
* System.out.println("+++ Initial center with intentional error offset: x="+IJ.
* d2s(centerXY0[0],3)+" y="+ IJ.d2s(centerXY0[1],3)); }
*/
double[] centerXY = correctedPatternCrossLocation(lwirReaderParameters, // LwirReaderParameters
// lwirReaderParameters, // null is OK
centerXY0, // initial coordinates of the pattern cross point
pattern[0][0], pattern[0][1], pattern[1][0], pattern[1][1], null, // correction
imp, // image data (Bayer mosaic)
distortionParameters, //
patternDetectParameters, matchSimulatedPatternCorr, // correlationSize
thisSimulParameters, equalizeGreens, windowFunctionCorr, // window function
windowFunctionCorr2, // window function
windowFunctionCorr4, // window function
simulationPattern, false, // is_lwir, // false, // if true - invert pattern TODO: Put is_lwir here?
null, // will create new instance of DoubleFHT class
distortionParameters.fastCorrelationOnFirstPass, locsNeib, debugLevel, dbgStr);
if (debugLevel > (debug_threshold - 1))
System.out.println("--- Initial center x=" + IJ.d2s(centerXY0[0], 3) + " y=" + IJ.d2s(centerXY0[1], 3)
+ " -> "
+ ((centerXY == null) ? " NULL " : (IJ.d2s(centerXY[0], 3) + " : " + IJ.d2s(centerXY[1], 3))));
// TODO: check statistics of correction - see if there is any dc bias
/*
* if ((debugLevel > (debug_threshold + 0)) && (centerXY != null)) { // trying
* second time - watch for conversion double []
* centerXY1=correctedPatternCrossLocation( lwirReaderParameters, //
* LwirReaderParameters lwirReaderParameters, // null is OK centerXY, // initial
* coordinates of the pattern cross point pattern[0][0], pattern[0][1],
* pattern[1][0], pattern[1][1], null, // correction imp, // image data (Bayer
* mosaic) distortionParameters, // patternDetectParameters,
* matchSimulatedPatternCorr, // correlationSize thisSimulParameters,
* equalizeGreens, windowFunctionCorr, // window function windowFunctionCorr2,
* // window function windowFunctionCorr4, // window function simulationPattern,
* false, // is_lwir, // false, // if true - invert pattern TODO: Put is_lwir
* here? null, // will create new instance of DoubleFHT class
* distortionParameters.fastCorrelationOnFirstPass, locsNeib, debugLevel,
* dbgStr);
* System.out.println("--- Second run: initial center x="+IJ.d2s(centerXY[0],3)
* +" y="+ IJ.d2s(centerXY[1],3)+
* " -> "+((centerXY1==null)?" NULL ":(IJ.d2s(centerXY1[0],3)+" : "+
* IJ.d2s(centerXY1[1],3))));
*
*
*
* }
*/
double[][] node = { centerXY, pattern[0], pattern[1] };
if (dbgStr != null) {
if (centerXY == null)
System.out.println(dbgStr + " correctedPatternCrossLocation->null");
else
System.out.println(dbgStr + " matchSimulatedPattern.findPattern->{{" + centerXY[0] + "," + centerXY[1]
+ "," + centerXY[2] + "}..}");
}
return node;
}
// ============= end of public double[][] tryPattern() ==================
/* ================================================================ */
// Optionally remove the outer (possibly corrupted) layer of the detected
// pattern nodes, extrapolate new layers of the nodes
// without pattern matching
public double[][][][] finalizeDistortionsBorder(
// final double [][][][] patternGrid,
final DistortionParameters distortionParameters, //
final boolean updateStatus, final int debug_level) {// debug level used inside loops
// double[][][][] patternGrid=this.PATTERN_GRID;
// final int [][] directionsUV= {{1,0},{0,1},{-1,0},{0,-1}}; // should have
// opposite direction shifted by half
final int[][] directionsUV8 = { { 1, 0 }, { 0, 1 }, { -1, 0 }, { 0, -1 }, { 1, 1 }, { -1, 1 }, { -1, -1 },
{ 1, -1 } }; // first 8 should be the same as in directionsUV
final List waveFrontList = new ArrayList(1000);
// create list of all nodes that have undefined neigbors (up/down/right/left)
int umax = 0, vmax = 0, vmin = this.PATTERN_GRID.length, umin = this.PATTERN_GRID[0].length;
for (int i = 0; i < this.PATTERN_GRID.length; i++)
for (int j = 0; j < this.PATTERN_GRID[i].length; j++) {
if ((this.PATTERN_GRID[i][j] != null) && (this.PATTERN_GRID[i][j][0] != null)) {
if (vmin > i)
vmin = i;
if (vmax < i)
vmax = i;
if (umin > j)
umin = j;
if (umax < j)
umax = j;
}
}
int[] uvNew = new int[2];
int[] iUV = new int[2];
int[] uvdir; // (u,v,direction}
double[][][] wave;
for (uvNew[1] = vmin; uvNew[1] <= vmax; uvNew[1]++)
for (uvNew[0] = umin; uvNew[0] <= umax; uvNew[0]++)
if (isCellDefined(this.PATTERN_GRID, uvNew)) {
for (int dir = 0; dir < directionsUV8.length; dir++) {
iUV[0] = uvNew[0] + directionsUV8[dir][0];
iUV[1] = uvNew[1] + directionsUV8[dir][1];
if (!isCellDefined(this.PATTERN_GRID, iUV)) {
putInWaveList(waveFrontList, uvNew, dir); // direction does not matter here
break;
}
}
}
final double[][] extrapolationWeights = generateWeights(distortionParameters.extrapolationSigma,
distortionParameters.correlationRadiusScale); // if 0 - use sigma as radius, inside - 1.0, outside 0.0.
// If >0 - size of array n*sigma
if (debugLevel > 1)
System.out.println("***** finalizeDistortionsBorder, initial wave length=" + waveFrontList.size());
// optionally remove outer (possibly corruopted) layer of nodes
if (distortionParameters.removeLast)
for (int i = 0; i < waveFrontList.size(); i++) {
if (distortionParameters.numberExtrapolated == 0)
invalidatePatternGridCell(this.PATTERN_GRID, getWaveList(waveFrontList, i));
else
initPatternGridCell(this.PATTERN_GRID, getWaveList(waveFrontList, i));
}
for (int layer = 0; (layer < distortionParameters.numberExtrapolated) && (waveFrontList.size() > 0); layer++) {
if ((layer > 0) || !distortionParameters.removeLast) { // build new layer around the current one
while (waveFrontList.size() > 0) { // will normally break out of the cycle
uvdir = getWaveList(waveFrontList, 0);
// if (this.PATTERN_GRID[uvdir[1]][uvdir[0]]==null) break; // finished adding
// new layer
if (!isCellDefined(this.PATTERN_GRID, uvdir))
break; // finished adding new layer, hit one of the newely added
for (int dir = 0; dir < directionsUV8.length; dir++) {
iUV[0] = uvdir[0] + directionsUV8[dir][0];
iUV[1] = uvdir[1] + directionsUV8[dir][1];
if ((iUV[0] < 0) || (iUV[1] < 0) || (iUV[0] >= distortionParameters.gridSize)
|| (iUV[1] >= distortionParameters.gridSize))
continue; // don't fit into UV grid
if (!isCellNew(this.PATTERN_GRID, iUV))
continue; // already processed
// add uv and dir to the list
putInWaveList(waveFrontList, iUV, dir); // direction is not used
initPatternGridCell(this.PATTERN_GRID, iUV);
// if (debugLevel>1) System.out.println("-->iUV= "+iUV[0]+", "+iUV[1]+",
// "+((dir+(directionsUV.length/2))%directionsUV.length));
}
waveFrontList.remove(0); // remove first element from the list
// if (debugLevel>1) System.out.println("xx> remove(0),
// (waveFrontList.size()="+(waveFrontList.size()));
}
}
// extrapolate x,y for the new layer of pixels (not yet using the new pixels in
// this layer)
if (updateStatus)
IJ.showStatus("Extrapolating border, layer " + (layer + 1) + ", length " + waveFrontList.size());
if (debugLevel > 1)
System.out.println("Extrapolating border, layer " + (layer + 1) + ", length " + waveFrontList.size());
wave = new double[waveFrontList.size()][][];
for (int i = 0; i < wave.length; i++) {
wave[i] = estimateCell(this.PATTERN_GRID, getWaveList(waveFrontList, i), extrapolationWeights, // quadrant
// of
// sample
// weights
true, // useContrast
!distortionParameters.useQuadratic, // use linear approximation (instead of quadratic)
1.0E-10, // thershold ratio of matrix determinant to norm for linear approximation (det
// too low - fail)
1.0E-20 // thershold ratio of matrix determinant to norm for quadratic approximation
// (det too low - fail)
);
if (wave[i] == null) { // try w/o contrast, just x,y
wave[i] = estimateCell(this.PATTERN_GRID, getWaveList(waveFrontList, i), extrapolationWeights, // quadrant
// of
// sample
// weights
false, // do not use Contrast, keep old contrast (even if it is NaN)
!distortionParameters.useQuadratic, // use linear approximation (instead of quadratic)
1.0E-10, // thershold ratio of matrix determinant to norm for linear approximation (det
// too low - fail)
1.0E-20 // thershold ratio of matrix determinant to norm for quadratic approximation
// (det too low - fail)
);
}
}
// set new values, removed failed cells (normally should not be any)
for (int i = wave.length - 1; i >= 0; i--) {
uvdir = getWaveList(waveFrontList, i);
this.PATTERN_GRID[uvdir[1]][uvdir[0]] = wave[i]; // null OK
if (wave[i] == null) {
if (debugLevel > 0)
System.out.println("Removing failed node (normally should not happen!), u=" + uvdir[0] + ", v="
+ uvdir[1]);
waveFrontList.remove(i);
}
}
}
return null;
}
/* ================================================================ */
// it now can start with non-empty Grid
public int distortions( // returns number of grid cells
final LwirReaderParameters lwirReaderParameters, // null is OK
final boolean[] triedIndices,
final DistortionParameters distortionParameters, //
final MatchSimulatedPattern.PatternDetectParameters patternDetectParameters,
final double min_half_period,
final double max_half_period,
final SimulationPattern.SimulParameters simulParameters,
final boolean equalizeGreens,
final ImagePlus imp, // image to process
final int threadsMax,
final boolean updateStatus,
final int debug_level, // debug level used inside loops
final int global_debug_level) {
if (imp == null)
return 0;
final int debugThreshold = 1;
final int sensor_type = LwirReaderParameters.sensorType(imp);
final int fft_size = distortionParameters.getFFTSize(sensor_type);
final int correlation_size = distortionParameters.getCorrelationSize(sensor_type);
final int minimal_pattern_cluster = distortionParameters.getMinimalPatternCluster(sensor_type);
final int[][] directionsUV = { { 1, 0 }, { 0, 1 }, { -1, 0 }, { 0, -1 } }; // should have opposite direction
final int[][] directionsUV8 = { { 1, 0 }, { 0, 1 }, { -1, 0 }, { 0, -1 }, { 1, 1 }, { -1, 1 }, { -1, -1 },
{ 1, -1 } }; // first 8 should be the same as in directionsUV
final int[] directionsBits8 = { 1, 4, 1, 4, 2, 8, 2, 8 }; // should match directionsUV8
int neibBits;
final Thread[] threads = newThreadArray(threadsMax);
final AtomicInteger cellNum = new AtomicInteger(0);
final List waveFrontList = new ArrayList(1000);
final int[] centerUV = { distortionParameters.gridSize / 2, distortionParameters.gridSize / 2 };
final double[][] locsNeib = calcNeibLocsWeights(distortionParameters, false); // no neighbors to average
Roi roi = imp.getRoi();
final Rectangle selection;
if (PATTERN_GRID == null) {
if (roi == null) {
selection = new Rectangle(0, 0, imp.getWidth(), imp.getHeight());
} else {
selection = (roi instanceof PointRoi) ? (new Rectangle(0, 0, imp.getWidth(), imp.getHeight()))
: roi.getBounds();
}
} else {
if ((getImageHeight() != imp.getHeight()) || (getImageWidth() != imp.getWidth())) {
String msg = "Supplied image does not match in dimensions " + imp.getWidth() + "x" + imp.getHeight()
+ " the one for wich grid was calculated (" + getImageWidth() + "x" + getImageHeight() + ")";
IJ.showMessage("Error", msg);
throw new IllegalArgumentException(msg);
}
selection = new Rectangle(0, 0, getImageWidth(), getImageHeight());
}
MatchSimulatedPattern matchSimulatedPattern = new MatchSimulatedPattern(fft_size);
matchSimulatedPattern.debugLevel = debugLevel;
MatchSimulatedPattern matchSimulatedPatternCorr = new MatchSimulatedPattern(correlation_size);
matchSimulatedPatternCorr.debugLevel = debugLevel;
final SimulationPattern.SimulParameters thisSimulParameters = simulParameters.clone();
thisSimulParameters.subdiv = distortionParameters.patternSubdiv;
thisSimulParameters.bPatternSigma = distortionParameters.bPatternSigma;
thisSimulParameters.barraySigma = distortionParameters.barraySigma;
SimulationPattern simulationPattern = new SimulationPattern(thisSimulParameters);
final double[] bPattern = simulationPattern.patternGenerator(simulParameters); // reuse pattern for next time
// find center of the selection (to be used to find initial pattern
// approximation)
if (debugLevel > 2)
System.out.println("bPattern.length=" + bPattern.length);
double[] windowFunction = initWindowFunction(fft_size, distortionParameters.fftGaussWidth);
// may need to decrease relative Gaussian width for larger windows
// public boolean absoluteCorrelationGaussWidth=false; // do not scale
// correlationGaussWidth when the FFT size is increased
/// (distortionParameters.absoluteCorrelationGaussWidth?0.5:1.0)
final double[] windowFunctionCorr = initWindowFunction(correlation_size,
distortionParameters.correlationGaussWidth, distortionParameters.zeros);
final double[] windowFunctionCorr2 = initWindowFunction(2 * correlation_size,
(distortionParameters.absoluteCorrelationGaussWidth ? 0.5 : 1.0)
* distortionParameters.correlationGaussWidth,
distortionParameters.zeros);
final double[] windowFunctionCorr4 = initWindowFunction(4 * correlation_size,
(distortionParameters.absoluteCorrelationGaussWidth ? 0.25 : 1.0)
* distortionParameters.correlationGaussWidth,
distortionParameters.zeros);
DistortionParameters thisDistortionParameters = distortionParameters.clone();
thisDistortionParameters.correlationMaxOffset = 0; // no verification of the offset here
thisDistortionParameters.correlationMinContrast = distortionParameters.correlationMinInitialContrast; // different
// contrast
// minimum
// here
thisDistortionParameters.correlationMinAbsoluteContrast = distortionParameters.correlationMinAbsoluteInitialContrast; // different
// contrast
// minimum
// here
int was_debug_level = debugLevel;
int[] iUV = new int[2];
final boolean updating = (PATTERN_GRID != null);
final boolean useFocusMask = updating && (focusMask != null); // do not expand wave beyound 1 grid step from
// needed image regions
Queue nodeQueue = new ConcurrentLinkedQueue();
boolean fromVeryBeginning = true;
if (!updating) {
for (int i = 3; i < triedIndices.length; i++)
if (triedIndices[i]) { // do not count first three NPE
fromVeryBeginning = false;
break;
}
double[] point = new double[2];
int tryHor = 0, tryVert = 0;
// distortionParameters.searchOverlap=goniometerParameters.searchOverlap;
// with distortionParameters.searchOverlap==0.5 (default) step will be FFTSize
// original pixels, so half of the (2xFFTSize) square processed simultaneously
if (distortionParameters.searchOverlap < 0.1)
distortionParameters.searchOverlap = 0.1;
int effectiveWidth = (int) (selection.width * 0.5 / distortionParameters.searchOverlap);
int effectiveHeight = (int) (selection.height * 0.5 / distortionParameters.searchOverlap);
for (int i = fft_size; i < effectiveWidth; i *= 2)
tryHor++;
for (int i = fft_size; i < effectiveHeight; i *= 2)
tryVert++;
int numTries = triedIndices.length - 1; // Should be equal to int numTries=1<<(tryHor+tryVert);
int nbv, nbh, nh, nv, nb;
if (debugLevel > 1)
System.out.println("selection.x=" + selection.x + " selection.y=" + selection.y + " selection.width="
+ selection.width + " selection.height=" + selection.height);
if (debugLevel > 1)
System.out.println("numTries=" + numTries + " tryHor=" + tryHor + " tryVert=" + tryVert);
boolean oldMode = false; // true; // false;
if (oldMode) { // old (single-threaded) mode
for (int startScanIndex = 3; startScanIndex <= numTries; startScanIndex++)
if (!triedIndices[startScanIndex]) {
if (startScanIndex == numTries) {
triedIndices[startScanIndex] = true; // all done
break;
}
nbh = tryHor - 1;
nbv = tryVert - 1;
nh = 0;
nv = 0;
nb = 0;
while (nb < (tryHor + tryVert)) {
if (nbh >= 0) {
if ((startScanIndex & (1 << nb)) != 0)
nh |= 1 << nbh;
nbh--;
nb++;
}
if (nbv >= 0) {
if ((startScanIndex & (1 << nb)) != 0)
nv |= 1 << nbv;
nbv--;
nb++;
}
}
if (debugLevel > 2)
System.out.println(
"Searching, n=" + startScanIndex + ", nv=" + nv + ", nh=" + nh + ", nb=" + nb);
if ((nv > 0) && (nh > 0)) {
point[0] = (selection.x + nh * selection.width / (1 << tryHor)) & ~1;
point[1] = (selection.y + nv * selection.height / (1 << tryVert)) & ~1;
if (debugLevel > 2)
System.out.println("trying xc=" + point[0] + ", yc=" + point[1] + "(nv=" + nv + ", nh="
+ nh + ")");
// System.out.println("### trying xc="+point[0]+", yc="+point[1]+"(nv="+nv+",
// nh="+nh+")");
if ((debugLevel > 2) && (startScanIndex == 3))
debugLevel = 3; // show debug images for the first point only
double[][] node = tryPattern(lwirReaderParameters, null, point, // xy to try
thisDistortionParameters, // no control of the displacement
patternDetectParameters, min_half_period, max_half_period, thisSimulParameters,
matchSimulatedPattern, matchSimulatedPatternCorr, simulationPattern, equalizeGreens,
imp, // image to process
bPattern, windowFunction, windowFunctionCorr, windowFunctionCorr2,
windowFunctionCorr4, locsNeib, // which neighbors to try (here - just the center)
null // dbgStr
);
debugLevel = was_debug_level;
if ((node != null) && (node[0] != null)) {
nodeQueue.add(new GridNode(startScanIndex,node));
break;
}
} else {
triedIndices[startScanIndex] = true; // only mark failed here, good ones will be marked when used
}
}
} else { // new multithreaded mode
int startScanIndex = 3;
for (; (startScanIndex < numTries) && triedIndices[startScanIndex]; startScanIndex++)
; // skip already tried indices
if ((global_debug_level > debugThreshold) && (startScanIndex > 3))
System.out.println("distortions(): startScanIndex=" + startScanIndex + " > 3 ####");
if (startScanIndex < numTries) {
nodeQueue = findPatternCandidates(
lwirReaderParameters, // LwirReaderParameters
triedIndices,
startScanIndex, // [0] will be updated
tryHor,
tryVert,
// numTries,
selection,
thisDistortionParameters, // no control of the displacement
patternDetectParameters,
min_half_period,
max_half_period,
thisSimulParameters,
matchSimulatedPattern,
matchSimulatedPatternCorr,
simulationPattern,
equalizeGreens,
imp, // image to process
bPattern,
windowFunction,
windowFunctionCorr,
windowFunctionCorr2,
windowFunctionCorr4,
locsNeib, // which neighbors to try (here - just the center)
threadsMax,
updateStatus,
this.debugLevel);
if (nodeQueue.isEmpty()) { // nodes==null){
// if (debugLevel>1) System.out.println("All start points tried");
if (global_debug_level > (debugThreshold + 1)) {
System.out.println("All start points tried");
int numLeft = 0;
for (boolean b : triedIndices)
if (!b)
numLeft++;
System.out.println("nodeQueue.isEmpty(), startScanIndex=" + startScanIndex + " numTries="
+ numTries + " numLeft=" + numLeft);
}
triedIndices[numTries] = true; // all tried
} else {
// if (debugLevel>1) System.out.println("Found "+nodes.length+" candidates");
// if (debugLevel>1) System.out.println("distortions: Found "+nodeQueue.size()+"
// candidates");
if (global_debug_level > debugThreshold) {
System.out.println("distortions: Found " + nodeQueue.size() + " candidates");
}
}
} else {
System.out.println("All start points tried before - should not get here");
triedIndices[numTries] = true; // all tried
}
}
// if (global_debug_level>0) System.out.println("distortions():
// startScanIndex="+startScanIndex);
// if (startScanIndex[0]>=numTries) startScanIndex[0]=-1; // all indices used
// if ((nodes==null) || (nodes[0]==null) || (nodes[0][0]==null)) {
if ((nodeQueue.isEmpty()) || (nodeQueue.peek().getNode()[0] == null)) {
if (debugLevel > 1)
System.out.println("*** Pattern not found");
this.PATTERN_GRID = null;
return 0;
}
if (global_debug_level > (debugThreshold + 1)) {
System.out.println("distortions(): found " + nodeQueue.size() + " grid candidates");
// System.out.println("*** distortions: Center x="+IJ.d2s(centerXY[0],3)+" y="+
// IJ.d2s(centerXY[1],3));
}
debugLevel = debug_level; // ????
} else { // create initial wave from the border nodes of existent grid
// start with clearing all invalid nodes
for (iUV[1] = 0; iUV[1] < this.PATTERN_GRID.length; iUV[1]++)
for (iUV[0] = 0; iUV[0] < this.PATTERN_GRID[0].length; iUV[0]++)
if (!isCellDefined(this.PATTERN_GRID, iUV))
clearPatternGridCell(this.PATTERN_GRID, iUV);
// int [] iUV= new int [2];
// probably start with clearing all invalid nodes
//// public boolean [] focusMask=null; // array matching image pixels, used with
// focusing (false outside sample areas)
int[] iUV1 = new int[2];
for (iUV[1] = 0; iUV[1] < this.PATTERN_GRID.length; iUV[1]++)
for (iUV[0] = 0; iUV[0] < this.PATTERN_GRID[0].length; iUV[0]++)
if (isCellDefined(this.PATTERN_GRID, iUV)) { // see if it has any new undefined neighbors
boolean hasNewNeib = false;
for (int dir = 0; dir < directionsUV.length; dir++) {
iUV1[0] = iUV[0] + directionsUV[dir][0];
iUV1[1] = iUV[1] + directionsUV[dir][1];
if (isCellNew(this.PATTERN_GRID, iUV1)) {
hasNewNeib = true;
break;
}
}
if (hasNewNeib) {
putInWaveList(waveFrontList, iUV, 0);
}
}
double[][] node = { null };
nodeQueue.add(new GridNode(-1,node)); // will not be used, any element
}
int numDefinedCells = 0;
int debug_left = nodeQueue.size();
for (GridNode gn : nodeQueue) { // trying candidates as grid seeds - until found or nothing left
// Only here mark triedIndices !!
if (gn.triedIndex >= 0) {
triedIndices[gn.triedIndex] = true; // mark the used seed
} else {
System.out.println("Was updating, gn.triedIndex(not used)="+gn.triedIndex);
}
debug_left--;
if (global_debug_level > (debugThreshold + 1)) {
System.out.println(
"distortions: nodeQueue has " + (debug_left) + " candidates left (excluding this one)");
}
if (!updating) {
double[][] node = gn.getNode();
double[] centerXY = node[0];
if (global_debug_level > debugThreshold) {
// System.out.println("distortions: node X/Y are "+centerXY[0]+"/"+centerXY[1]);
System.out.println("distortions: nodeQueue has " + (debug_left)
+ " candidates left (excluding this one) :node X/Y are " + centerXY[0] + "/" + centerXY[1]);
}
// if (debugLevel>1) {
if (global_debug_level > (debugThreshold + 1)) {
System.out.println(
"*** distortions: Center x=" + IJ.d2s(centerXY[0], 3) + " y=" + IJ.d2s(centerXY[1], 3));
System.out.println("*** distortions: setting debugX=" + IJ.d2s(centerXY[0], 3) + " debugY="
+ IJ.d2s(centerXY[1], 3));
patternDetectParameters.debugX = centerXY[0]; // Change debug coordinates to the initial node
patternDetectParameters.debugY = centerXY[1]; // patternDetectParameters.debugRadius);
}
debugLevel = debug_level;
// Reset pattern grid
this.PATTERN_GRID = setPatternGridArray(distortionParameters.gridSize); // global to be used with
// threads?
setPatternGridCell(this.PATTERN_GRID, centerUV, centerXY, // contrast OK?
node[1], node[2]);
waveFrontList.clear();
putInWaveList(waveFrontList, centerUV, 0);
if (global_debug_level > debugThreshold) { // 1) {
System.out.println("putInWaveList(waveFrontList, {" + centerUV[0] + "," + centerUV[1] + "}, 0);");
}
}
// Each layer processing may be multi-threaded, they join before going to the
// next layer
// When looking for the next cells, the position is estimated knowing the
// neighbor that has wave vectors defined
// after the layer pass is over, the wave vectors are calculated from the
// distances to neighbors (one or both vectors may have
// to use those from the neighbor?
if (debugLevel > 1)
System.out.println("-->centerUV= " + centerUV[0] + ", " + centerUV[1] + ", 0");
int[] uvdir; // (u,v,direction}
int layer = 0;
int dir;
double[][][] neibors = new double[8][][]; // uv and xy vectors to 8 neibors (some may be null
double[][] thisCell;
double[][] otherCell;
// final int debugThreshold=2;
final double[][] extrapolationWeights = generateWeights(distortionParameters.correlationWeightSigma,
distortionParameters.correlationRadiusScale); // if 0 - use sigma as radius, inside - 1.0, outside
// 0.0. If >0 - size of array n*sigma
int umax, vmax, vmin, umin;
final AtomicInteger addedCells = new AtomicInteger(0); // cells added at cleanup stage
final AtomicBoolean cleanup = new AtomicBoolean(false); // after the wave dies, it will be restored for all
// cells with defined neigbors to try again. maybe -
// try w/o threads?
final AtomicInteger debugCellSet = new AtomicInteger(0); // cells added at cleanup stage
// special case (most common, actually) when initial wave has 1 node. Remove it
// after processing
// first cell(s) will need large correction and so may fail during "refine", so
// trying to recalculate it right after the first layer)
ArrayList initialWave = new ArrayList();
for (Integer I : waveFrontList)
initialWave.add(I);
while (waveFrontList.size() > 0) {
// process current list, add new wave layer (moving in one of the 4 directions)
// proceed until the entry is undefined on the grid (or list is empty
while (waveFrontList.size() > 0) { // will normally break out of the cycle
uvdir = getWaveList(waveFrontList, 0);
if (this.debugLevel > (debugThreshold + 1))
System.out.println("<--uvdir= " + uvdir[0] + ", " + uvdir[1] + ", " + uvdir[2]);
if (this.PATTERN_GRID[uvdir[1]][uvdir[0]] == null)
break; // finished adding new layer
if (!isCellDefined(this.PATTERN_GRID, uvdir))
break; // finished adding new layer, hit one of the newely added
boolean hasNeededNeighbor = true;
if (useFocusMask) {
int ix = (int) Math.round(this.PATTERN_GRID[uvdir[1]][uvdir[0]][0][0]);
int iy = (int) Math.round(this.PATTERN_GRID[uvdir[1]][uvdir[0]][0][1]);
int indx = iy * getImageWidth() + ix;
if ((indx < 0) || (indx > focusMask.length)) {
System.out.println("distortions(): this.PATTERN_GRID[" + uvdir[1] + "][" + uvdir[0]
+ "][0][0]=" + this.PATTERN_GRID[uvdir[1]][uvdir[0]][0][0]);
System.out.println("distortions(): this.PATTERN_GRID[" + uvdir[1] + "][" + uvdir[0]
+ "][0][1]=" + this.PATTERN_GRID[uvdir[1]][uvdir[0]][0][1]);
System.out.println("distortions(): ix=" + ix);
System.out.println("distortions(): iy=" + iy);
System.out.println("distortions(): focusMask.length=" + focusMask.length);
}
// TODO: find how it could get negative coordinates
if ((ix < 0) || (iy < 0) || (ix >= distortionParameters.gridSize)
|| (iy >= distortionParameters.gridSize))
hasNeededNeighbor = false; // ???
else
hasNeededNeighbor = focusMask[iy * getImageWidth() + ix]; // * OOB -1624
// java.lang.ArrayIndexOutOfBoundsException:
// -1624, at
// MatchSimulatedPattern.distortions(MatchSimulatedPattern.java:3063),
// at
// LensAdjustment.updateFocusGrid(LensAdjustment.java:121),
// at
// Aberration_Calibration.measurePSFMetrics(Aberration_Calibration.java:5994)
}
for (dir = 0; dir < directionsUV.length; dir++) {
iUV[0] = uvdir[0] + directionsUV[dir][0];
iUV[1] = uvdir[1] + directionsUV[dir][1];
if ((iUV[0] < 0) || (iUV[1] < 0) || (iUV[0] >= distortionParameters.gridSize)
|| (iUV[1] >= distortionParameters.gridSize))
continue; // don't fit into UV grid
if (!isCellNew(PATTERN_GRID, iUV))
continue; // already processed (or deleted!)
// add uv and dir to the list
// public boolean [] focusMask=null; // array matching image pixels, used with
// focusing (false outside sample areas)
// New: if it is updating the grid and focusMask is defined - do not go more
// than 1 step away from the needed image area
if (hasNeededNeighbor) {
putInWaveList(waveFrontList, iUV, (dir + (directionsUV.length / 2)) % directionsUV.length); // opposite
// direction
initPatternGridCell(PATTERN_GRID, iUV);
if (debugLevel > 1)
System.out.println("-->iUV= " + iUV[0] + ", " + iUV[1] + ", "
+ ((dir + (directionsUV.length / 2)) % directionsUV.length));
}
}
waveFrontList.remove(0); // remove first element from the list
if (debugLevel > 1)
System.out.println("xx> remove(0), (waveFrontList.size()=" + (waveFrontList.size()));
}
if (waveFrontList.size() == 0)
break; // not really needed?
layer++;
if (updateStatus)
IJ.showStatus("Correlating patterns, layer " + layer + (cleanup.get() ? "(cleanup)" : "")
+ ", length " + waveFrontList.size());
// if (debugLevel>1) System.out.println("Correlating patterns, layer "+layer+",
// length "+waveFrontList.size());
if (global_debug_level > (debugThreshold + 1))
System.out.println("Correlating patterns, layer " + layer + ", length " + waveFrontList.size());
// starting layer
cellNum.set(0);
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() {
@Override
public void run() {
SimulationPattern simulationPattern = new SimulationPattern(bPattern);
MatchSimulatedPattern matchSimulatedPatternCorr = new MatchSimulatedPattern(correlation_size);
// distortionParameters.correlationSize);
DoubleFHT fht_instance = new DoubleFHT(); // provide DoubleFHT instance to save on
// initializations (or null)
String dbgStr = "";
for (int ncell = cellNum.getAndIncrement(); ncell < waveFrontList.size(); ncell = cellNum
.getAndIncrement()) {
int[] iUVdir = getWaveList(waveFrontList, ncell);
if (debugLevel > debugThreshold) {
dbgStr = "";
dbgStr += "<--iUVdir= " + iUVdir[0] + ", " + iUVdir[1] + ", " + iUVdir[2];
}
int[] iUVRef = new int[2];
iUVRef[0] = iUVdir[0] + directionsUV[iUVdir[2]][0];
iUVRef[1] = iUVdir[1] + directionsUV[iUVdir[2]][1];
// refCell - is where it came from, but if the initials are disabled, it is null
double[][] refCell = PATTERN_GRID[iUVRef[1]][iUVRef[0]]; // should never be null as it
// is an old one
if (refCell == null) {
System.out.println("**** refCell==null - what does it mean?**** u=" + iUVRef[0]
+ " v=" + iUVRef[1] + " current=" + iUVdir[0] + "/" + iUVdir[1] + " len="
+ iUVdir.length);
continue;
} else if ((refCell[0] != null) && (refCell[0].length > 3)) {
double dbg_contrast = (refCell[0].length > 2) ? refCell[0][2] : Double.NaN;
if (debugLevel > debugThreshold) {
System.out.println("**** refCell was deleted **** u=" + iUVRef[0] + " v="
+ iUVRef[1] + " current=" + iUVdir[0] + "/" + iUVdir[1] + " ncell=" + ncell
+ " waveFrontList.size()=" + waveFrontList.size() + " ref_x="
+ IJ.d2s(refCell[0][0], 3) + " ref_y=" + IJ.d2s(refCell[0][1], 3)
+ " contrast=" + IJ.d2s(dbg_contrast, 3));
}
}
// found reference cell, calculate x/y, make sure it is inside the selection w/o
// borders
double[][] wv = new double[2][];
wv[0] = refCell[1];
wv[1] = refCell[2];
double[][] uv2xy = matrix2x2_invert(wv);
double[] dUV = new double[2];
dUV[0] = 0.5 * (iUVdir[0] - iUVRef[0]);
dUV[1] = 0.5 * (iUVdir[1] - iUVRef[1]);
double[] dXY = matrix2x2_mul(uv2xy, dUV);
double[] expectedXY = matrix2x2_add(refCell[0], dXY);
// Try new extrapolation, debug print both
// extrapolationWeights
double[][] estimatedCell = estimateCell(PATTERN_GRID, iUVdir, extrapolationWeights, // quadrant
// of
// sample
// weights
true, // useContrast
!distortionParameters.useQuadratic, // use linear approximation (instead of
// quadratic)
1.0E-10, // thershold ratio of matrix determinant to norm for linear
// approximation (det too low - fail)
1.0E-20 // thershold ratio of matrix determinant to norm for quadratic
// approximation (det too low - fail)
);
double[][] simulPars = null;
if (debugLevel > debugThreshold) {
dbgStr += " ExpectedXY(old)= " + IJ.d2s(expectedXY[0], 3) + " / "
+ IJ.d2s(expectedXY[1], 3) + ", " + " vw00=" + IJ.d2s(wv[0][0], 5)
+ " vw01=" + IJ.d2s(wv[0][1], 5) + " vw10=" + IJ.d2s(wv[1][0], 5) + " vw11="
+ IJ.d2s(wv[1][1], 5);
if (estimatedCell == null) {
dbgStr += " -- ExpectedXY(new)= ***** NULL **** ";
} else {
dbgStr += " -- ExpectedXY(new)= " + IJ.d2s(estimatedCell[0][0], 3) + " / "
+ IJ.d2s(estimatedCell[0][1], 3) + ", " + " vw00="
+ IJ.d2s(estimatedCell[1][0], 5) + " vw01="
+ IJ.d2s(estimatedCell[1][1], 5) + " vw10="
+ IJ.d2s(estimatedCell[2][0], 5) + " vw11="
+ IJ.d2s(estimatedCell[2][1], 5);
}
}
if (estimatedCell != null) {
expectedXY = estimatedCell[0];
wv[0] = estimatedCell[1];
wv[1] = estimatedCell[2];
simulPars = getSimulationParametersFromGrid(PATTERN_GRID, iUVdir, // U,V of the
// center point
// (for which
// the
// simulation
// pattern
// should be
// built
expectedXY, // x,y of the center point (or null to use grid)
extrapolationWeights, // quadrant of sample weights
!distortionParameters.useQuadratic, // use linear approximation (instead of
// quadratic)
1.0E-10, // thershold ratio of matrix determinant to norm for linear
// approximation (det too low - fail)
1.0E-20 // thershold ratio of matrix determinant to norm for quadratic
// approximation (det too low - fail)
);
if (debugLevel > debugThreshold) {
dbgStr += " {" + IJ.d2s(simulPars[0][0], 5) + "/" + IJ.d2s(simulPars[0][1], 5)
+ "/" + IJ.d2s(simulPars[0][2], 5);
if (simulPars[0].length > 3)
dbgStr += "/" + IJ.d2s(simulPars[0][3], 7) + "/"
+ IJ.d2s(simulPars[0][4], 7) + "/" + IJ.d2s(simulPars[0][5], 7)
+ "}";
dbgStr += " {" + IJ.d2s(simulPars[1][0], 5) + "/" + IJ.d2s(simulPars[1][1], 5)
+ "/" + IJ.d2s(simulPars[1][2], 5);
if (simulPars[1].length > 3)
dbgStr += "/" + IJ.d2s(simulPars[1][3], 7) + "/"
+ IJ.d2s(simulPars[1][4], 7) + "/" + IJ.d2s(simulPars[1][5], 7)
+ "}";
}
}
if (!selection.contains((int) Math.round(expectedXY[0]),
(int) Math.round(expectedXY[1]))) { // just the center point
invalidatePatternGridCell(PATTERN_GRID, iUVdir);
if (debugLevel > debugThreshold) {
dbgStr += " -- not in selection ";
System.out.println(dbgStr);
}
continue; // the correlation selection does not fit into WOI selection
}
// Proceed with correlation
// TODO: add contrast verification ? Maximal distance from expected? (return
// null if failed)
double[] centerXY = correctedPatternCrossLocation(lwirReaderParameters, expectedXY, // initial
// coordinates
// of
// the
// pattern
// cross
// point
wv[0][0], wv[0][1], wv[1][0], wv[1][1], simulPars, imp, // image data (Bayer
// mosaic)
distortionParameters, //
patternDetectParameters, matchSimulatedPatternCorr, // correlationSize
thisSimulParameters, equalizeGreens, windowFunctionCorr, windowFunctionCorr2,
windowFunctionCorr4, simulationPattern, ((iUVdir[0] ^ iUVdir[1]) & 1) != 0, // if
// true
// -
// invert
// pattern
fht_instance, distortionParameters.fastCorrelationOnFirstPass, locsNeib,
debugLevel, null);
// System.out.println("*+*debugLevel="+debugLevel);
if (centerXY == null) {
invalidatePatternGridCell(PATTERN_GRID, iUVdir);
if (debugLevel > debugThreshold) {
dbgStr += " -- FAILED";
System.out.println(dbgStr);
}
continue; // failed to find pattern in the cell TODO: implement
}
if (debugCellSet.getAndIncrement() == 0) { // First cell
if (passNumber == 1) {
debugUV[0] = iUVdir[0];
debugUV[1] = iUVdir[1];
if (debugLevel > debugThreshold)
System.out.println(
"debugUV[] set to {" + debugUV[0] + "," + debugUV[1] + "}");
passNumber = 2; // global passNumber
}
}
// Found new cell, save info and increment counter
setPatternGridCell(PATTERN_GRID, iUVdir, centerXY,
// specify wave vectors from the parent cell, will recalculate (if possible)
wv[0], // null, // double [] wv1,
wv[1]); // null); // double [] wv2);
if (cleanup.get())
addedCells.getAndIncrement();
if (debugLevel > debugThreshold) { // was no "-2"
dbgStr += "==>added" + iUVdir[0] + "/" + iUVdir[1] + ", dir" + iUVdir[2];
System.out.println(dbgStr);
}
}
}
};
}
startAndJoin(threads);
// remove invalid cells from the list
for (int i = waveFrontList.size() - 1; i >= 0; i--) {
if (!isCellValid(PATTERN_GRID, getWaveList(waveFrontList, i))) {
// Make that cell "new", so it will be tried again, until wave will not touch
// it. So when more neigbors will be defined, previously failed
// cell will be retried
clearPatternGridCell(PATTERN_GRID, getWaveList(waveFrontList, i));
waveFrontList.remove(i);
if (debugLevel > (debugThreshold + 1))
System.out.println("XXX->clear invalid (" + i + ")");
}
}
// If anything was added during the layer - calculate and fill in wave vectors
// here (they are set to the same as in the parent cell)
// this code is not needed now, the wave vectors are recalculated from x/y
// locations, the stored ones are not used
if (waveFrontList.size() > 0) {
for (int listIndex = 0; listIndex < waveFrontList.size(); listIndex++) {
uvdir = getWaveList(waveFrontList, listIndex);
if (debugLevel > (debugThreshold + 1))
System.out.println("<---= uvdir= " + uvdir[0] + ", " + uvdir[1] + ", " + uvdir[2]);
thisCell = PATTERN_GRID[uvdir[1]][uvdir[0]];
neibBits = 0;
for (dir = 0; dir < directionsUV8.length; dir++) {
neibors[dir] = null;
iUV[0] = uvdir[0] + directionsUV8[dir][0];
iUV[1] = uvdir[1] + directionsUV8[dir][1];
if ((iUV[0] < 0) || (iUV[1] < 0) || (iUV[0] >= distortionParameters.gridSize)
|| (iUV[1] >= distortionParameters.gridSize))
continue; // don't fit into UV grid
if (isCellValid(PATTERN_GRID, iUV)) {
neibors[dir] = new double[2][2];
otherCell = PATTERN_GRID[iUV[1]][iUV[0]];
neibors[dir][0][0] = 0.5 * directionsUV8[dir][0]; // u
neibors[dir][0][1] = 0.5 * directionsUV8[dir][1]; // v
neibors[dir][1][0] = otherCell[0][0] - thisCell[0][0]; // x
neibors[dir][1][1] = otherCell[0][1] - thisCell[0][1]; // y
neibBits |= directionsBits8[dir];
}
}
int i = Integer.bitCount(neibBits);
if (debugLevel > (debugThreshold + 1))
System.out.println("neibBits=" + neibBits + ", number of bits= " + i);
if (i > 1) {
double[][] wv = waveVectorsFromNeib(neibors);
setPatternGridCell(PATTERN_GRID, uvdir, null, // XY already set
wv[0], wv[1]);
if (debugLevel > (debugThreshold + 1))
System.out.println("==+> number of bits:" + i + " vw00=" + IJ.d2s(wv[0][0], 5)
+ " vw01=" + IJ.d2s(wv[0][1], 5) + " vw10=" + IJ.d2s(wv[1][0], 5) + " vw11="
+ IJ.d2s(wv[1][1], 5));
// wv= WaveVectorsFromNeib(neibors);
//
// vectors: [num_vector][0][0] - U
// [num_vector][0][1] - V
// [num_vector][1][0] - X
// [num_vector][1][1] - Y
// [num_vector] == null - skip
//
}
}
} else if (initialWave != null) {
if ((global_debug_level > (debugThreshold + 1)) && (initialWave != null)) {
System.out.println("No sense to initiate clenaup during first layer"); // problems heer?
}
} else if (!cleanup.get() || (addedCells.get() > 0)) { // create list of the defined cells on the border
// (if wave died)
cleanup.set(true);
// debug
// if ((global_debug_level>0) && (initialWave!=null)) {
// System.out.println("clenaup during first layer"); // problems heer?
// System.out.println("Added "+addedCells.get()+" during border cleanup on first
// layer");
// }
if ((debugLevel > (debugThreshold + 1)) && !cleanup.get())
System.out.println("Added " + addedCells.get() + " during border cleanup"); // can not get here
addedCells.set(0);
umax = 0;
vmax = 0;
vmin = PATTERN_GRID.length;
umin = PATTERN_GRID[0].length;
for (int i = 0; i < PATTERN_GRID.length; i++)
for (int j = 0; j < PATTERN_GRID[i].length; j++) {
if ((PATTERN_GRID[i][j] != null) && (PATTERN_GRID[i][j][0] != null)) {
if (vmin > i)
vmin = i;
if (vmax < i)
vmax = i;
if (umin > j)
umin = j;
if (umax < j)
umax = j;
}
}
int[] uvNew = new int[2];
for (uvNew[1] = vmin; uvNew[1] <= vmax; uvNew[1]++)
for (uvNew[0] = umin; uvNew[0] <= umax; uvNew[0]++)
if (isCellDefined(PATTERN_GRID, uvNew)) {
for (dir = 0; dir < directionsUV.length; dir++) {
iUV[0] = uvNew[0] + directionsUV[dir][0];
iUV[1] = uvNew[1] + directionsUV[dir][1];
if (!isCellDefined(PATTERN_GRID, iUV) && !isCellDeleted(PATTERN_GRID, iUV)) {
putInWaveList(waveFrontList, uvNew, dir); // direction does not matter here
break;
}
}
}
if (global_debug_level > (debugThreshold + 1))
System.out.println("***** Starting cleanup, wave length=" + waveFrontList.size()); // ????
}
// end of layer - it is a hack below, marking initial wave to recalculate it
// from neighbors
if (initialWave != null) { // just after the first layer (usually one cell) - delete it and add next
// time - otherwise first one needs large correction
if (global_debug_level > (debugThreshold + 1)) {
System.out.println("Removing " + initialWave.size()
+ " initial wave cells, waveFrontList.size()=" + waveFrontList.size());
for (int listIndex = 0; listIndex < waveFrontList.size(); listIndex++) {
int[] dbg_uvdir = getWaveList(waveFrontList, listIndex);
System.out.println("waveFrontList[" + listIndex + "]: " + dbg_uvdir[0] + "/" + dbg_uvdir[1]
+ " dir=" + dbg_uvdir[2]);
}
}
while (initialWave.size() > 0) {
uvdir = getWaveList(initialWave, 0);
// clearPatternGridCell(PATTERN_GRID, uvdir);
if (global_debug_level > (debugThreshold + 1))
System.out.println("Removing x=" + uvdir[0] + " y=" + uvdir[1] + " dir=" + uvdir[2]);
markDeletedPatternGridCell(PATTERN_GRID, uvdir);
initialWave.remove(0);
}
initialWave = null;
}
} // while (waveFrontList.size()>0)
debugLevel = was_debug_level;
/*
* if (updating){ return PATTERN_GRID; // no need to crop the array, it should
* not change }
*/
umax = 0;
vmax = 0;
vmin = PATTERN_GRID.length;
umin = PATTERN_GRID[0].length;
numDefinedCells = 0;
for (int i = 0; i < PATTERN_GRID.length; i++)
for (int j = 0; j < PATTERN_GRID[i].length; j++) {
if ((PATTERN_GRID[i][j] != null) && (PATTERN_GRID[i][j][0] != null)) {
if (vmin > i)
vmin = i;
if (vmax < i)
vmax = i;
if (umin > j)
umin = j;
if (umax < j)
umax = j;
numDefinedCells++;
}
}
// if (updating){
// return numDefinedCells; // no need to crop the array, it should not change
// }
if (!updating) {
if (vmin > vmax) {
this.PATTERN_GRID = null;
continue; // try next in queue if available
// return 0; // null; // nothing found
}
// Add extra margins for future extrapolation
int extra = distortionParameters.numberExtrapolated - ((distortionParameters.removeLast) ? 1 : 0);
vmin -= extra;
if (vmin < 0)
vmin = 0;
umin -= extra;
if (umin < 0)
umin = 0;
vmax += extra;
if (vmax >= PATTERN_GRID.length)
vmax = PATTERN_GRID.length - 1;
umax += extra;
if (umax >= PATTERN_GRID[0].length)
umax = PATTERN_GRID[0].length - 1;
// make sure the odd/even uv does not change (and so the cross phases defined by
// U & V)
vmin &= ~1;
umin &= ~1;
// make width/height even (not needed)
umax |= 1;
vmax |= 1;
// remove margins
double[][][][] result = new double[vmax - vmin + 1][umax - umin + 1][][];
for (int i = vmin; i <= vmax; i++)
for (int j = umin; j <= umax; j++) {
if ((PATTERN_GRID[i][j] != null) && (PATTERN_GRID[i][j][0] != null)) {
result[i - vmin][j - umin] = PATTERN_GRID[i][j];
} else
result[i - vmin][j - umin] = null;
}
this.debugUV[0] -= umin;
this.debugUV[1] -= umin;
if (debugLevel > (debugThreshold + 2))
System.out.println("debugUV[] updated to {" + this.debugUV[0] + "," + this.debugUV[1] + "}");
if (debugLevel > (debugThreshold + 1))
System.out.println("Total number of defined cells=" + numDefinedCells);
this.PATTERN_GRID = result;
}
// more tests here (moved from the caller) that result is good
double averageGridPeriod = Double.NaN;
double[] gridPeriods = { Double.NaN, Double.NaN };
if (this.PATTERN_GRID != null) {
averageGridPeriod = averageGridPeriod(this.PATTERN_GRID);
gridPeriods = averageGridPeriods(this.PATTERN_GRID); // {min,max}
}
if (debugLevel > debugThreshold) {
System.out.println("Pattern period=" + averageGridPeriod + " {" + gridPeriods[0] + "," + gridPeriods[1]
+ "}" + " limits are set to :" + patternDetectParameters.minGridPeriod + ","
+ patternDetectParameters.maxGridPeriod);
}
if (!Double.isNaN(averageGridPeriod)) {
if (!Double.isNaN(patternDetectParameters.minGridPeriod)
&& (patternDetectParameters.minGridPeriod > 0.0)
&& (averageGridPeriod < patternDetectParameters.minGridPeriod)) {
if (debugLevel > 0) {
System.out.println("Pattern is too small, period=" + averageGridPeriod + " minimal="
+ patternDetectParameters.minGridPeriod);
}
continue; // bad grid
}
if (!Double.isNaN(patternDetectParameters.maxGridPeriod)
&& (patternDetectParameters.maxGridPeriod > 0.0)
&& (averageGridPeriod > patternDetectParameters.maxGridPeriod)) {
if (debugLevel > 0) {
System.out.println("Pattern is too large, period=" + averageGridPeriod + " maximal="
+ patternDetectParameters.maxGridPeriod);
}
continue; // bad grid
}
}
if ((minimal_pattern_cluster <= 0) || // minimal cluster size is disabled
(distortionParameters.scaleMinimalInitialContrast <= 0) || // minimal cluster size is disabled
((numDefinedCells == 0) && fromVeryBeginning) || // no cells detected at all, starting from the very
// beginning
(numDefinedCells >= minimal_pattern_cluster) // detected enough cells
) {
return numDefinedCells;
}
if ((numDefinedCells < minimal_pattern_cluster) && (numDefinedCells > 10)) // detected enough cells
{
if (global_debug_level > (debugThreshold - 1)) {
System.out.println("***** Initial cluster has " + numDefinedCells + " cells that is less than "
+ "minimal_pattern_cluster = " + minimal_pattern_cluster + " *****");
}
// return numDefinedCells;
}
if ((roi != null) && !(roi instanceof PointRoi)) { // don't use this feature with ROI as it can be small
if (global_debug_level > 0)
System.out.println(
"Initial pattern cluster is small (" + numDefinedCells + "), but ROI is set - no retries");
{
return numDefinedCells;
}
}
} // next node in queue
return 0; // none
}
// ================= end of public int distortions() ===================
public double[][] findPatternCandidate_old(
// final int [] startScanIndex, // [0] will be updated
final boolean[] triedIndices, // which indices are already tried
final int startScanIndex, final int tryHor, final int tryVert,
// final int numTries,
final Rectangle selection, final DistortionParameters distortionParameters, //
final MatchSimulatedPattern.PatternDetectParameters patternDetectParameters, final double min_half_period,
final double max_half_period, final SimulationPattern.SimulParameters thisSimulParameters,
final MatchSimulatedPattern matchSimulatedPattern, final MatchSimulatedPattern matchSimulatedPatternCorr,
final SimulationPattern simulationPattern, final boolean equalizeGreens, final ImagePlus imp, // image to
// process
final double[] bPattern, final double[] windowFunction, final double[] windowFunctionCorr,
final double[] windowFunctionCorr2, final double[] windowFunctionCorr4, final double[][] locsNeib, // which
// neighbors
// to
// try
// (here
// -
// just
// the
// center)
final int threadsMax, final boolean updateStatus, final int debugLevel) {
final Thread[] threads = newThreadArray(threadsMax);
// final AtomicInteger seqNumber = new AtomicInteger(3);
// final AtomicInteger seqNumber = new AtomicInteger(startScanIndex[0]);
final AtomicInteger seqNumber = new AtomicInteger(startScanIndex);
// startScanIndex
final AtomicBoolean nodeSet = new AtomicBoolean(false);
final double[][][] nodeRef = new double[1][][];
nodeRef[0] = null;
// System.out.println("===== findPatternCandidate():
// startScanIndex="+startScanIndex);
// for (int i=0;i= 0) {
if ((n & (1 << nb)) != 0)
nh |= 1 << nbh;
nbh--;
nb++;
}
if (nbv >= 0) {
if ((n & (1 << nb)) != 0)
nv |= 1 << nbv;
nbv--;
nb++;
}
}
if (debugLevel > 2)
System.out.println("Searching, n=" + n + ", nv=" + nv + ", nh=" + nh + ", nb=" + nb);
if ((nv > 0) && (nh > 0)) {
point[0] = (selection.x + nh * selection.width / (1 << tryHor)) & ~1;
point[1] = (selection.y + nv * selection.height / (1 << tryVert)) & ~1;
if (debugLevel > 2)
System.out.println("trying xc=" + point[0] + ", yc=" + point[1] + "(nv=" + nv
+ ", nh=" + nh + ")");
// if ((debugLevel>2) && (n==3)) debugLevel=3; // show debug images for the
// first point
double[][] node = tryPattern(null, // LwirReaderParameters lwirReaderParameters, // null
// is OK
doubleFHT, point, // xy to try
distortionParameters, // no control of the displacement
patternDetectParameters, min_half_period, max_half_period, thisSimulParameters,
matchSimulatedPattern, matchSimulatedPatternCorr, simulationPattern,
equalizeGreens, imp, // image to process
bPattern, windowFunction, windowFunctionCorr, windowFunctionCorr2,
windowFunctionCorr4, locsNeib, // which neibors to try (here - just the center)
null // dbgStr
);
if ((node != null) && (node[0] != null)) {
if (nodeSet.compareAndSet(false, true)) {
nodeRef[0] = node;
// startScanIndex[0]=seqNumber.get();
triedIndices[n] = true; // found and will be processed
if (debugLevel > 1)
System.out.println("probing " + n);
break;
} else {
if (debugLevel > 1)
System.out.println("missed " + n);
}
} else {
triedIndices[n] = true; // tried, but nothing found
// System.out.println("empty "+n);
}
} else {
triedIndices[n] = true; // tried, but nothing found
// System.out.println("wrong "+n);
}
}
}
};
}
startAndJoin(threads);
// if (nodeRef[0]==null) startScanIndex[0]=numTries; // all used
return nodeRef[0];
}
class GridNode {
int triedIndex; // index in the list of tried points
double[][] node;
public GridNode(
int triedIndex,
double[][] node) {
this.triedIndex = triedIndex;
this.node = node;
}
public double[][] getNode() {
return this.node;
}
}
private Queue findPatternCandidates(
final LwirReaderParameters lwirReaderParameters, // null is OK
final boolean[] triedIndices, // which indices are already tried
final int startScanIndex,
final int tryHor,
final int tryVert,
final Rectangle selection,
final DistortionParameters distortionParameters, //
final MatchSimulatedPattern.PatternDetectParameters patternDetectParameters,
final double min_half_period,
final double max_half_period,
final SimulationPattern.SimulParameters thisSimulParameters,
final MatchSimulatedPattern matchSimulatedPattern,
final MatchSimulatedPattern matchSimulatedPatternCorr,
final SimulationPattern simulationPattern,
final boolean equalizeGreens,
final ImagePlus imp, // image to process
final double[] bPattern,
final double[] windowFunction,
final double[] windowFunctionCorr,
final double[] windowFunctionCorr2,
final double[] windowFunctionCorr4,
final double[][] locsNeib, // which neibors to try (here - just the center)
final int threadsMax,
final boolean updateStatus,
final int debugLevel) {
final int debugThreshold = 1; // -1; // 1; ** Restore 1
if ((debugThreshold < 0) || (debugLevel < -10000)) {
System.out.println("findPatternCandidates(): debugThreshold < 0 - restore when done");
}
if ((debugLevel > debugThreshold) && ((debugLevel > 1) || (startScanIndex > 3))) {
int debugNumLeft = 0;
for (boolean b : triedIndices)
if (!b)
debugNumLeft++;
System.out.println("findPatternCandidates(), startScanIndex= " + startScanIndex + ",triedIndices.length="
+ triedIndices.length + " debugNumLeft=" + debugNumLeft);
}
final Thread[] threads = newThreadArray(threadsMax);
final AtomicInteger seqNumber = new AtomicInteger(startScanIndex);
final AtomicInteger debugNumThreadAtomic = new AtomicInteger(0);
final Queue nodeQueue = new ConcurrentLinkedQueue();
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() {
@Override
public void run() {
int nbh, nbv, nh, nv, nb;
double[] point = new double[2];
DoubleFHT doubleFHT = new DoubleFHT();
int debugNumThread = debugNumThreadAtomic.getAndIncrement();
for (int n = seqNumber.getAndIncrement(); n < (triedIndices.length - 1); n = seqNumber
.getAndIncrement())
if (!triedIndices[n]) {
if (!nodeQueue.isEmpty())
break; // already set at least one element - does it work? Here - one per thread
nbh = tryHor - 1;
nbv = tryVert - 1;
nh = 0;
nv = 0;
nb = 0;
while (nb < (tryHor + tryVert)) {
if (nbh >= 0) {
if ((n & (1 << nb)) != 0)
nh |= 1 << nbh;
nbh--;
nb++;
}
if (nbv >= 0) {
if ((n & (1 << nb)) != 0)
nv |= 1 << nbv;
nbv--;
nb++;
}
}
if (debugLevel > 2)
System.out.println("Searching, n=" + n + ", nv=" + nv + ", nh=" + nh + ", nb=" + nb);
if ((nv > 0) && (nh > 0)) {
point[0] = (selection.x + nh * selection.width / (1 << tryHor)) & ~1;
point[1] = (selection.y + nv * selection.height / (1 << tryVert)) & ~1;
if (debugLevel > 1) // was 2
System.out.println("trying xc=" + point[0] + ", yc=" + point[1] + "(nv=" + nv
+ ", nh=" + nh + ")");
if (debugLevel > 1) // was 2
System.out.println(debugNumThread + ":" + n + " >> ");
double[][] node = tryPattern(
lwirReaderParameters, // LwirReaderParameters lwirReaderParameters, null is OK
doubleFHT,
point, // xy to try
distortionParameters, // no control of the displacement
patternDetectParameters,
min_half_period,
max_half_period,
thisSimulParameters,
matchSimulatedPattern,
matchSimulatedPatternCorr,
simulationPattern,
equalizeGreens,
imp, // image to process
bPattern,
windowFunction,
windowFunctionCorr,
windowFunctionCorr2,
windowFunctionCorr4,
locsNeib, // which neighbors to try (here - just the center)
(debugLevel > debugThreshold)
? ("" + debugNumThread + ":" + n + ", nv=" + nv + ", nh=" + nh + ", nb="
+ nb + " " + point[0] + "/" + point[1])
: null);
if ((node != null) && (node[0] != null)) {
nodeQueue.add(new GridNode(n, node)); // save tried index for later, will be marked only when used (fixing a very old bug)
if (debugLevel > debugThreshold)
System.out.println("adding candidate " + n + " x0=" + point[0] + " y0="
+ point[1] + " -> " + node[0][0] + "/" + node[0][1]
+ " seqNumber.get()=" + seqNumber.get() + " n=" + n);
continue; // so triedIndices[n] will not be set true
}
} else {
if (debugLevel > debugThreshold)
System.out
.println("-----" + debugNumThread + ":" + n + ", nv=" + nv + ", nh=" + nh);
}
// triedIndices[n] = true; // regardless - good or bad - that was wrong, and led to skipping retries of good (but not first) nodes
triedIndices[n] = true; // will come here only for failed nodes.
}
}
};
}
startAndJoin(threads);
if (debugLevel > debugThreshold) {
System.out.println("seqNumber after join is " + seqNumber.get());
}
if (seqNumber.get() >= (triedIndices.length - 1))
triedIndices[triedIndices.length - 1] = true; // all tried
return nodeQueue; // never null, may be empty
}
/* ================================================================ */
public void scaleContrast(double scale) {
for (double[][][] patternRow : this.PATTERN_GRID) {
if (patternRow != null)
for (double[][] node : patternRow) {
if ((node != null) && (node.length > 0) && (node[0] != null) && (node[0].length > 2)) {
node[0][2] *= scale;
}
}
}
}
/* ================================================================ */
public double refineDistortionCorrelation(
final LwirReaderParameters lwirReaderParameters, // null is OK
final DistortionParameters distortionParameters, //
final MatchSimulatedPattern.PatternDetectParameters patternDetectParameters,
final SimulationPattern.SimulParameters simulParameters,
final boolean equalizeGreens,
final ImagePlus imp, // image to process
final double maxCorr, // maximal allowed correction, in pixels (0.0) - any
final int threadsMax,
final boolean updateStatus,
final int debug_level) {// debug level used inside loops
scaleContrast(distortionParameters.scaleFirstPassContrast);
final int sensor_type = LwirReaderParameters.sensorType(imp);
final double[][][][] patternGrid = this.PATTERN_GRID;
final int debugThreshold = 1;
final Rectangle selection = new Rectangle(0, 0, imp.getWidth(), imp.getHeight());
final int correlation_size = distortionParameters.getCorrelationSize(sensor_type);
MatchSimulatedPattern matchSimulatedPatternCorr = new MatchSimulatedPattern(correlation_size);
// distortionParameters.correlationSize);
matchSimulatedPatternCorr.debugLevel = debugLevel;
SimulationPattern simulationPattern = new SimulationPattern();
final SimulationPattern.SimulParameters thisSimulParameters = simulParameters.clone();
thisSimulParameters.subdiv = distortionParameters.patternSubdiv;
final double[] bPattern = simulationPattern.patternGenerator(simulParameters); // reuse pattern for next time
final double[] windowFunctionCorr = initWindowFunction(correlation_size, // distortionParameters.correlationSize,
distortionParameters.correlationGaussWidth, distortionParameters.zeros);
final double[] windowFunctionCorr2 = initWindowFunction(2 * correlation_size, // distortionParameters.correlationSize,
(distortionParameters.absoluteCorrelationGaussWidth ? 0.5 : 1.0)
* distortionParameters.correlationGaussWidth,
distortionParameters.zeros);
final double[] windowFunctionCorr4 = initWindowFunction(4 * correlation_size, // distortionParameters.correlationSize,
(distortionParameters.absoluteCorrelationGaussWidth ? 0.25 : 1.0)
* distortionParameters.correlationGaussWidth,
distortionParameters.zeros);
final int height = patternGrid.length;
final int width = (height > 0) ? patternGrid[0].length : 0; // oob 0??
final Thread[] threads = newThreadArray(threadsMax);
int was_debug_level = debugLevel;
final int debugOnLevel = (debug_level > 0) ? 3 : 0;
final double[][] locsNeib = calcNeibLocsWeights(distortionParameters,
distortionParameters.correlationAverageOnRefine);
debugLevel = debug_level;
if (debugLevel > 1)
System.out.println("Refining correlations, width= " + width + ", height= " + height);
final double[][] extrapolationWeights = generateWeights(distortionParameters.correlationWeightSigma,
distortionParameters.correlationRadiusScale); // if 0 - use sigma as radius, inside - 1.0, outside 0.0.
// If >0 - size of array n*sigma
int i = -1;
int[] iUV = new int[2];
for (i = 0; i < (width * height); i++) {
iUV[0] = i % width;
iUV[1] = i / width;
if (isCellDefined(patternGrid, iUV))
break;
}
if (i < 0)
return Double.NaN; // no defined nodes at all
final int startCell = i;
final AtomicInteger cellNum = new AtomicInteger(startCell);
final AtomicInteger cellNumDoneAtomic = new AtomicInteger(startCell);
int dc = i;
// Debug only
if (debugOnLevel > debugThreshold) {
final int[][] directionsUV8 = { { 1, 0 }, { 0, 1 }, { -1, 0 }, { 0, -1 }, { 1, 1 }, { -1, 1 }, { -1, -1 },
{ 1, -1 } }; // first 8 should be the same as in directionsUV
for (i = dc; i < (width * height); i++) {
iUV[0] = i % width;
iUV[1] = i / width;
if ((iUV[0] > 0) && (iUV[1] > 0) && (iUV[0] < (width - 1)) && (iUV[1] < (height - 1))
&& isCellDefined(patternGrid, iUV)
&& isCellDefined(patternGrid, matrix2x2_add(iUV, directionsUV8[0]))
&& isCellDefined(patternGrid, matrix2x2_add(iUV, directionsUV8[1]))
&& isCellDefined(patternGrid, matrix2x2_add(iUV, directionsUV8[2]))
&& isCellDefined(patternGrid, matrix2x2_add(iUV, directionsUV8[3]))
&& isCellDefined(patternGrid, matrix2x2_add(iUV, directionsUV8[4]))
&& isCellDefined(patternGrid, matrix2x2_add(iUV, directionsUV8[5]))
&& isCellDefined(patternGrid, matrix2x2_add(iUV, directionsUV8[6]))
&& isCellDefined(patternGrid, matrix2x2_add(iUV, directionsUV8[7]))) {
dc = i;
System.out.println("not used: debug U=" + iUV[0] + " V=" + iUV[1] + " index=" + dc);
break;
}
}
}
final int debugCell = debugUV[0] + debugUV[1] * width;
/**
* That was wrong to update currently calculated grid, so the newGrid will be
* calculated instead, then copied altogether
*/
final double[][][] newGrid = new double[height][width][];
final boolean refineInPlace = distortionParameters.refineInPlace;
for (int v = 0; v < height; v++)
for (int u = 0; u < width; u++)
newGrid[v][u] = null;
IJ.showProgress(0);
if (updateStatus)
IJ.showStatus("Refining correlations");
// MinMaxSync minMaxSync=new MinMaxSync();
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() {
@Override
public void run() {
SimulationPattern simulationPattern = new SimulationPattern(bPattern);
MatchSimulatedPattern matchSimulatedPatternCorr = new MatchSimulatedPattern(correlation_size); // distortionParameters.correlationSize);
DoubleFHT fht_instance = new DoubleFHT(); // provide DoubleFHT instance to save on initializations
// (or null)
int[] iUV = new int[2];
boolean nowDebugCell = false;
for (int ncell = cellNum.getAndIncrement(); ncell < (width * height); ncell = cellNum
.getAndIncrement()) {
nowDebugCell = (ncell == debugCell);
int thisDebug = (nowDebugCell) ? debugOnLevel : debugLevel;
iUV[0] = ncell % width;
iUV[1] = ncell / width;
if (nowDebugCell && (thisDebug > 1))
System.out.println(">>>>>>>>>>> Debug cell, thisDebug=" + thisDebug + ", iUV={" + iUV[0]
+ "," + iUV[1] + "}");
// if ((updateStatus) && (iUV[0]==0)) IJ.showStatus("Refining correlations, row
// "+(iUV[1]+1)+" of "+height);
if ((thisDebug > 1) && ((iUV[0] == 0) || (nowDebugCell)))
System.out.println("Refining correlations, row " + (iUV[1] + 1) + " of " + height);
if (!isCellDefined(patternGrid, iUV)) {
cellNumDoneAtomic.getAndIncrement();
continue;
}
Rectangle centerCross = correlationSelection(patternGrid[iUV[1]][iUV[0]][0], // initial
// coordinates
// of the
// pattern cross
// point
correlation_size/2); // distortionParameters.correlationSize);
if (!selection.contains(centerCross)) {
cellNumDoneAtomic.getAndIncrement();
continue; // the correlation selection does not fit into WOI selection ??? WOI is now full
// image
}
// Proceed with correlation
// TODO: add contrast verification ? Maximal distance from expected? (return
// null if failed)
double[][] simulPars = getSimulationParametersFromGrid(PATTERN_GRID, iUV, // U,V of the center
// point (for which
// the simulation
// pattern should be
// built
null, // x,y of the center point (or null to use grid)
extrapolationWeights, // quadrant of sample weights
!distortionParameters.useQuadratic, // use linear approximation (instead of quadratic)
1.0E-10, // thershold ratio of matrix determinant to norm for linear approximation (det
// too low - fail)
1.0E-20 // thershold ratio of matrix determinant to norm for quadratic approximation
// (det too low - fail)
);
if ((thisDebug > debugThreshold) && (simulPars != null)) {
String dbgStr = "";
dbgStr += " {" + IJ.d2s(simulPars[0][0], 5) + "/" + IJ.d2s(simulPars[0][1], 5) + "/"
+ IJ.d2s(simulPars[0][2], 5);
if (simulPars[0].length > 3)
dbgStr += "/" + IJ.d2s(simulPars[0][3], 7) + "/" + IJ.d2s(simulPars[0][4], 7) + "/"
+ IJ.d2s(simulPars[0][5], 7) + "}";
dbgStr += " {" + IJ.d2s(simulPars[1][0], 5) + "/" + IJ.d2s(simulPars[1][1], 5) + "/"
+ IJ.d2s(simulPars[1][2], 5);
if (simulPars[1].length > 3)
dbgStr += "/" + IJ.d2s(simulPars[1][3], 7) + "/" + IJ.d2s(simulPars[1][4], 7) + "/"
+ IJ.d2s(simulPars[1][5], 7) + "}";
System.out.println(dbgStr);
if (nowDebugCell && (thisDebug > 3)) {
double[] XY = { PATTERN_GRID[iUV[1]][iUV[0]][0][0] - 32.0,
PATTERN_GRID[iUV[1]][iUV[0]][0][1] - 32.0 };
System.out.println("iUV[0]=" + iUV[0] + "iUV[1]=" + iUV[1]);
System.out.println("CC : " + IJ.d2s(PATTERN_GRID[iUV[1]][iUV[0]][0][0] - XY[0], 3) + "/"
+ IJ.d2s(PATTERN_GRID[iUV[1]][iUV[0]][0][1] - XY[1], 3));
System.out
.println("TL : " + IJ.d2s(PATTERN_GRID[iUV[1] - 1][iUV[0] - 1][0][0] - XY[0], 3)
+ "/" + IJ.d2s(PATTERN_GRID[iUV[1] - 1][iUV[0] - 1][0][1] - XY[1], 3)); // sometimes
// throws
System.out.println("TC : " + IJ.d2s(PATTERN_GRID[iUV[1] - 1][iUV[0]][0][0] - XY[0], 3)
+ "/" + IJ.d2s(PATTERN_GRID[iUV[1] - 1][iUV[0]][0][1] - XY[1], 3));
System.out
.println("TR : " + IJ.d2s(PATTERN_GRID[iUV[1] - 1][iUV[0] + 1][0][0] - XY[0], 3)
+ "/" + IJ.d2s(PATTERN_GRID[iUV[1] - 1][iUV[0] + 1][0][1] - XY[1], 3));
System.out.println("CR : " + IJ.d2s(PATTERN_GRID[iUV[1]][iUV[0] + 1][0][0] - XY[0], 3)
+ "/" + IJ.d2s(PATTERN_GRID[iUV[1]][iUV[0] + 1][0][1] - XY[1], 3));
System.out
.println("BR : " + IJ.d2s(PATTERN_GRID[iUV[1] + 1][iUV[0] + 1][0][0] - XY[0], 3)
+ "/" + IJ.d2s(PATTERN_GRID[iUV[1] + 1][iUV[0] + 1][0][1] - XY[1], 3));
System.out.println("BC : " + IJ.d2s(PATTERN_GRID[iUV[1] + 1][iUV[0]][0][0] - XY[0], 3)
+ "/" + IJ.d2s(PATTERN_GRID[iUV[1] + 1][iUV[0]][0][1] - XY[1], 3));
System.out
.println("BL : " + IJ.d2s(PATTERN_GRID[iUV[1] + 1][iUV[0] - 1][0][0] - XY[0], 3)
+ "/" + IJ.d2s(PATTERN_GRID[iUV[1] + 1][iUV[0] - 1][0][1] - XY[1], 3));
System.out.println("CL : " + IJ.d2s(PATTERN_GRID[iUV[1]][iUV[0] - 1][0][0] - XY[0], 3)
+ "/" + IJ.d2s(PATTERN_GRID[iUV[1]][iUV[0] - 1][0][1] - XY[1], 3));
System.out.println("CC : " + IJ.d2s(PATTERN_GRID[iUV[1]][iUV[0]][0][0], 3) + "/"
+ IJ.d2s(PATTERN_GRID[iUV[1]][iUV[0]][0][1], 3));
System.out.println("TL : " + IJ.d2s(PATTERN_GRID[iUV[1] - 1][iUV[0] - 1][0][0], 3) + "/"
+ IJ.d2s(PATTERN_GRID[iUV[1] - 1][iUV[0] - 1][0][1], 3));
System.out.println("TC : " + IJ.d2s(PATTERN_GRID[iUV[1] - 1][iUV[0]][0][0], 3) + "/"
+ IJ.d2s(PATTERN_GRID[iUV[1] - 1][iUV[0]][0][1], 3));
System.out.println("TR : " + IJ.d2s(PATTERN_GRID[iUV[1] - 1][iUV[0] + 1][0][0], 3) + "/"
+ IJ.d2s(PATTERN_GRID[iUV[1] - 1][iUV[0] + 1][0][1], 3));
System.out.println("CR : " + IJ.d2s(PATTERN_GRID[iUV[1]][iUV[0] + 1][0][0], 3) + "/"
+ IJ.d2s(PATTERN_GRID[iUV[1]][iUV[0] + 1][0][1], 3));
System.out.println("BR : " + IJ.d2s(PATTERN_GRID[iUV[1] + 1][iUV[0] + 1][0][0], 3) + "/"
+ IJ.d2s(PATTERN_GRID[iUV[1] + 1][iUV[0] + 1][0][1], 3));
System.out.println("BC : " + IJ.d2s(PATTERN_GRID[iUV[1] + 1][iUV[0]][0][0], 3) + "/"
+ IJ.d2s(PATTERN_GRID[iUV[1] + 1][iUV[0]][0][1], 3));
System.out.println("BL : " + IJ.d2s(PATTERN_GRID[iUV[1] + 1][iUV[0] - 1][0][0], 3) + "/"
+ IJ.d2s(PATTERN_GRID[iUV[1] + 1][iUV[0] - 1][0][1], 3));
System.out.println("CL : " + IJ.d2s(PATTERN_GRID[iUV[1]][iUV[0] - 1][0][0], 3) + "/"
+ IJ.d2s(PATTERN_GRID[iUV[1]][iUV[0] - 1][0][1], 3));
}
}
double[] centerXY = correctedPatternCrossLocation(
lwirReaderParameters, // LwirReaderParameters lwirReaderParameters, null is OK
patternGrid[iUV[1]][iUV[0]][0], // initial coordinates of the pattern cross point
patternGrid[iUV[1]][iUV[0]][1][0], patternGrid[iUV[1]][iUV[0]][1][1],
patternGrid[iUV[1]][iUV[0]][2][0], patternGrid[iUV[1]][iUV[0]][2][1], simulPars, imp, // image data (Bayer mosaic)
distortionParameters, //
patternDetectParameters,
matchSimulatedPatternCorr, // correlationSize
thisSimulParameters,
equalizeGreens,
windowFunctionCorr,
windowFunctionCorr2,
windowFunctionCorr4,
simulationPattern, ((iUV[0] ^ iUV[1]) & 1) != 0, // if true - invert pattern
fht_instance,
distortionParameters.fastCorrelationOnFinalPass, //
locsNeib,
thisDebug, // thisDebug
null);
if (centerXY != null) {
if (thisDebug > 2)
System.out.println("==>iUV={" + iUV[0] + ", " + iUV[1] + "}. "
+ patternGrid[iUV[1]][iUV[0]][0][0] + " / " + patternGrid[iUV[1]][iUV[0]][0][1]
+ " -> " + centerXY[0] + " / " + centerXY[1]);
// refine should provide higher contrast than was there, it is so for EO, but not for LWIR
// LWIR refined contrast is approximately the same as that of non-refined, twice less than EO
// boosting it here
if (sensor_type == 1) {
centerXY[2] *= 2.0; // boosting refined LWIR
}
if (refineInPlace)
setPatternGridCell(patternGrid, iUV, centerXY, null, // double [] wv1,
null); // double [] wv2);
else
newGrid[iUV[1]][iUV[0]] = centerXY.clone();
} else {
if (debug_level > 0) {
System.out.println("refineDistortionCorrelation(): failed to refine grid for U="
+ iUV[0] + " V=" + iUV[1] + " X=" + patternGrid[iUV[1]][iUV[0]][0][0] + " Y="
+ patternGrid[iUV[1]][iUV[0]][0][1]);
}
}
final int numFinished = cellNumDoneAtomic.getAndIncrement();
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
// Here, we can safely update the GUI
// because we'll be called from the
// event dispatch thread
IJ.showProgress(numFinished, width * height - 1);
}
});
}
}
};
}
startAndJoin(threads);
IJ.showProgress(1.0); // turn off
double maxActualCorr = 0.0;
double dx, dy, dist;
if (!refineInPlace) {
// maxCorr
if (maxCorr > 0.0) {
// make sure there are no new undefined cells that were initially defined and no
// correction more than the limit
int numUndefined = 0, numFar = 0;
// double maxCorr2=maxCorr*maxCorr;
for (iUV[1] = 0; iUV[1] < height; iUV[1]++)
for (iUV[0] = 0; iUV[0] < width; iUV[0]++)
if (isCellDefined(patternGrid, iUV)) {
if (newGrid[iUV[1]][iUV[0]] == null) {
numUndefined++;
} else {
dx = newGrid[iUV[1]][iUV[0]][0] - patternGrid[iUV[1]][iUV[0]][0][0];
dy = newGrid[iUV[1]][iUV[0]][1] - patternGrid[iUV[1]][iUV[0]][0][1];
dist = Math.sqrt(dx * dx + dy * dy);
if (dist > maxActualCorr)
maxActualCorr = dist;
if (dist > maxCorr) {
numFar++;
newGrid[iUV[1]][iUV[0]] = null;
}
}
}
if ((numUndefined > 0) || (numFar > 0)) {
if (debug_level > 0) {
System.out.println(
"refineDistortionCorrelation(): failed, number of undefined cells=" + numUndefined
+ ", number of too far cells=" + numFar + " maxActualCorr=" + maxActualCorr);
}
if (numUndefined > 0)
return -numUndefined; // negative - some cells undefined, no info about maximal correction
// returned
return maxActualCorr; // no correction performed
}
} else {
// only calculate maximal distance
for (iUV[1] = 0; iUV[1] < height; iUV[1]++)
for (iUV[0] = 0; iUV[0] < width; iUV[0]++)
if (isCellDefined(patternGrid, iUV) && (newGrid[iUV[1]][iUV[0]] != null)) {
dx = newGrid[iUV[1]][iUV[0]][0] - patternGrid[iUV[1]][iUV[0]][0][0];
dy = newGrid[iUV[1]][iUV[0]][1] - patternGrid[iUV[1]][iUV[0]][0][1];
dist = Math.sqrt(dx * dx + dy * dy);
if (dist > maxActualCorr)
maxActualCorr = dist;
}
}
// Copy new values for the grid cells
boolean debug_bias = true;
double lwir_refine_dx = -0.25;
double lwir_refine_dy = -0.25;
double sw = 0.0, swx = 0.0, swy = 0.0; // finding x,y bias of refining
for (iUV[1] = 0; iUV[1] < height; iUV[1]++)
for (iUV[0] = 0; iUV[0] < width; iUV[0]++)
if (newGrid[iUV[1]][iUV[0]] != null) {
if (debug_bias) {
double [] center_refined = newGrid[iUV[1]][iUV[0]];
double [] center_orig = patternGrid[iUV[1]][iUV[0]][0];
sw += center_refined[2];
swx += center_refined[2] * (center_refined[0] - center_orig[0]);
swy += center_refined[2] * (center_refined[1] - center_orig[1]);
}
setPatternGridCell(patternGrid, iUV, newGrid[iUV[1]][iUV[0]], null, // double [] wv1,
null); // double [] wv2);
} else if (sensor_type == 1) { // FIXME: correcting LWIR refine bias
if (isCellDefined(patternGrid, iUV)) {
patternGrid[iUV[1]][iUV[0]][0][0] += lwir_refine_dx;
patternGrid[iUV[1]][iUV[0]][0][1] += lwir_refine_dy;
}
}
if (debug_bias && (sw > 0.0)) {
swx /= sw;
swy /= sw;
System.out.println("Refine bias dx = "+swx+"pix, dy = "+swy+"pix");
}
// correction is only calculated for simultaneous update (not for in-place)
if (debug_level > 1) {
System.out.println("refineDistortionCorrelation(): maximal correction=" + maxActualCorr + " pixels");
}
}
debugLevel = was_debug_level;
return maxActualCorr;
}
public class MinMaxSync {
private double min;
private double max;
private boolean defined;
public MinMaxSync() {
defined = false;
min = Double.NaN;
;
max = Double.NaN;
;
}
public void reset() {
defined = false;
}
public synchronized void minMax(double d) {
if (!defined) {
min = d;
max = d;
defined = true;
} else {
if (d > max)
max = d;
else if (d < min)
min = d;
}
}
public double getMin() {
return min;
}
public double getMax() {
return max;
}
public boolean isDefined() {
return defined;
}
}
/* ================================================================ */
/*
* public boolean flatFieldCorrection=true; // compensate grid uneven intensity
* (vignetting, illumination) public double flatFieldExtarpolate=1.0; //
* extrapolate flat field intensity map (relative to the average grid period)
* public double flatFieldBlur=1.0; // blur the intensity map (relative to the
* average grid period)
*
*/
public ImagePlus equalizeGridIntensity(
ImagePlus imp,
double[][][][] patternGrid,
DistortionParameters distortionParameters, //
boolean equalizeGreens,
int debugLevel,
boolean updateStatus,
int threadsMax) {
int dbgThreshold = 1;
final int sensor_type = LwirReaderParameters.sensorType(imp);
double[][] gridIntensity = calcGridIntensity(4, // bayerComponent
distortionParameters.getCorrelationSize(sensor_type), // correlationSize, // size
distortionParameters, //
equalizeGreens, imp, // image to process
patternGrid, threadsMax);// debug level used inside loops
if (debugLevel > (dbgThreshold + 2)) {
double[] testGI = new double[gridIntensity.length * gridIntensity[0].length];
int index = 0;
for (int v = 0; v < gridIntensity.length; v++)
for (int u = 0; u < gridIntensity[0].length; u++)
testGI[index++] = gridIntensity[v][u];
ShowDoubleFloatArrays.showArrays(testGI, gridIntensity[0].length, gridIntensity.length,
imp.getTitle() + "-GI");
}
double[] fffg = calcFlatFieldForGrid(gridIntensity, patternGrid, imp.getWidth(), imp.getHeight());
double averageGridPeriod = averageGridPeriod(patternGrid);
// if (debugLevel > (dbgThreshold + 2)) {
// ShowDoubleFloatArrays.showArrays(fffg, imp.getWidth(), imp.getHeight(),
// imp.getTitle() + "-fftg");
// }
int preShrink = (int) (averageGridPeriod * distortionParameters.flatFieldShrink);
int expand = (int) (averageGridPeriod * distortionParameters.flatFieldExpand);
double extrapolateSigma = averageGridPeriod * distortionParameters.flatFieldSigmaRadius;
double extrapolateKSigma = distortionParameters.flatFieldExtraRadius;
if (debugLevel >= (dbgThreshold + 2)) {
ShowDoubleFloatArrays.showArrays(fffg.clone(), imp.getWidth(), imp.getHeight(), imp.getTitle() + "-fffg");
}
extrapolatePatternFlatFieldCorrection(fffg, // fieldXY,
imp.getWidth(), preShrink, expand, extrapolateSigma, extrapolateKSigma, threadsMax, // 100; // testing
// multi-threading,
// limit maximal
// number of threads
updateStatus);
if (debugLevel > (dbgThreshold + 2)) {
ShowDoubleFloatArrays.showArrays(fffg.clone(), imp.getWidth(), imp.getHeight(),
imp.getTitle() + "-extrapolated");
}
if (distortionParameters.flatFieldBlur > 0.0) {
DoubleGaussianBlur gb = new DoubleGaussianBlur();
gb.blurDouble(fffg, imp.getWidth(), imp.getHeight(), distortionParameters.flatFieldBlur * averageGridPeriod,
distortionParameters.flatFieldBlur * averageGridPeriod, 0.01);
}
double max = 0.0;
for (int i = 0; i < fffg.length; i++)
if (max < fffg[i])
max = fffg[i];
double k = 1.0 / max;
for (int i = 0; i < fffg.length; i++) {
fffg[i] *= k;
if (fffg[i] < distortionParameters.flatFieldMin)
fffg[i] = 0.0;
}
if (debugLevel > 1)
System.out.println("averageGridPeriod=" + averageGridPeriod);
if (debugLevel > (dbgThreshold + 1)) {
ShowDoubleFloatArrays.showArrays(fffg, imp.getWidth(), imp.getHeight(), imp.getTitle() + "-blured");
}
this.flatFieldForGrid = fffg;
ImagePlus imp_eq = applyFlatField(imp, fffg);
if (debugLevel > dbgThreshold)
imp_eq.show();
return imp_eq;
}
public ImagePlus applyFlatField(ImagePlus imp) {
if (this.PATTERN_GRID == null)
return imp;
if ((getImageHeight() != imp.getHeight()) || (getImageWidth() != imp.getWidth())) {
String msg = "applyFlatField (): Supplied image does not match in dimensions " + imp.getWidth() + "x"
+ imp.getHeight() + " the one for wich grid was calculated (" + getImageWidth() + "x"
+ getImageHeight() + ")";
// IJ.showMessage("Error",msg);
throw new IllegalArgumentException(msg);
}
return applyFlatField(imp, this.flatFieldForGrid);
}
public ImagePlus applyFlatField(ImagePlus imp, double[] ff) {
if (ff == null)
return imp; // nothing to apply
float[] pixels = (float[]) imp.getProcessor().getPixels();
if (pixels.length != ff.length) {
String msg = "Supplied image does not match in dimensions " + (pixels.length)
+ " the one for wich grid was calculated (" + (ff.length) + ")";
// IJ.showMessage("Error",msg);
throw new IllegalArgumentException(msg);
}
float[] eqPixels = new float[pixels.length];
for (int i = 0; i < pixels.length; i++)
if (ff[i] > 0)
eqPixels[i] = (float) (pixels[i] / ff[i]);
else
eqPixels[i] = 0.0f;
ImageProcessor ip = new FloatProcessor(imp.getWidth(), imp.getHeight());
ip.setPixels(eqPixels);
ip.resetMinAndMax();
ImagePlus imp_eq = new ImagePlus(imp.getTitle() + "-flat", ip);
return imp_eq;
}
public double[][][] calcGridIntensities(final DistortionParameters distortionParameters, //
final boolean equalizeGreens, final ImagePlus imp, // image to process
final int threadsMax) {
double dSize = averageGridPeriod(this.PATTERN_GRID) * distortionParameters.averagingAreaScale;
int size = ~1 & ((int) Math.round(dSize)); // should be even
int size4 = ~1 & ((int) Math.round(dSize * Math.sqrt(2.0))); // larger when using diagonal greens (component 4)
int[] bayerIndices = { -1, 1, 4, 2 }; // -1 - contrast, 1 - R, 4 - G, 2 - B
this.gridContrastBrightness = new double[bayerIndices.length][][];
if (this.debugLevel > 1)
System.out.println("Calculating grid intensities, average period="
+ IJ.d2s(averageGridPeriod(this.PATTERN_GRID), 2) + " pixels, using square sample " + size + "x"
+ size + " for all colors ,but (diagonal) greens, for greens - " + size4 + "x" + size4);
for (int i = 0; i < bayerIndices.length; i++) {
this.gridContrastBrightness[i] = calcGridIntensity(bayerIndices[i], // final int bayerComponent,
((bayerIndices[i] == 4) ? size4 : size), // final int size,
distortionParameters, // final DistortionParameters distortionParameters, //
equalizeGreens, imp, // image to process
this.PATTERN_GRID, threadsMax);
}
return this.gridContrastBrightness;
}
public double[][] calcGridIntensity(
final int bayerComponent,
final int size,
final DistortionParameters distortionParameters, //
final boolean equalizeGreens,
final ImagePlus imp, // image to process
final double[][][][] patternGrid,
final int threadsMax) {// debug level used inside loops
final int sensor_type = LwirReaderParameters.sensorType(imp);
final double[][] gridIntensity = new double[patternGrid.length][patternGrid[0].length];
for (int i = 0; i < gridIntensity.length; i++)
for (int j = 0; j < gridIntensity[0].length; j++)
gridIntensity[i][j] = (bayerComponent >= 0) ? -1.0 : 0.0; // undefined
MatchSimulatedPattern matchSimulatedPatternCorr = new MatchSimulatedPattern(
distortionParameters.getCorrelationSize(sensor_type)); // correlationSize);
matchSimulatedPatternCorr.debugLevel = debugLevel;
final double[] windowFunctionCorr = initWindowFunction(size, // distortionParameters.correlationSize,
distortionParameters.correlationGaussWidth, distortionParameters.zeros);
final int width = patternGrid[0].length;
final int height = patternGrid.length;
final Thread[] threads = newThreadArray(threadsMax);
if (debugLevel > 1)
System.out.println("Calculating average intensity at grid nodes, width= " + width + ", height= " + height
+ ", bayerComponent=" + bayerComponent + ", size=" + size);
int[] iUV = new int[2];
int i;
for (i = 0; i < (width * height); i++) {
iUV[0] = i % width;
iUV[1] = i / width;
if (isCellDefined(patternGrid, iUV))
break;
}
final int startCell = i;
final AtomicInteger cellNum = new AtomicInteger(startCell);
final double[][][] newGrid = new double[height][width][];
for (int v = 0; v < height; v++)
for (int u = 0; u < width; u++)
newGrid[v][u] = null;
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() {
@Override
public void run() {
int[] iUV = new int[2];
for (int ncell = cellNum.getAndIncrement(); ncell < (width * height); ncell = cellNum
.getAndIncrement()) {
iUV[0] = ncell % width;
iUV[1] = ncell / width;
if (!isCellDefined(patternGrid, iUV))
continue;
if (bayerComponent >= 0) {
Rectangle centerCross = correlationSelection(patternGrid[iUV[1]][iUV[0]][0], // initial
// coordinates
// of the
// pattern
// cross
// point
size); // distortionParameters.correlationSize);
double[][] input_bayer = splitBayer(imp, centerCross, equalizeGreens);
double sum = 0.0, sumW = 0.0;
for (int i = 0; i < input_bayer[bayerComponent].length; i++) {
sum += input_bayer[bayerComponent][i] * windowFunctionCorr[i];
sumW += windowFunctionCorr[i];
}
gridIntensity[iUV[1]][iUV[0]] = sum / sumW;
} else {
// trying alternative
// double [][][][] patternGrid_same=patternGrid;
gridIntensity[iUV[1]][iUV[0]] = Double.NaN;
if (isCellDefined(patternGrid, iUV[0], iUV[1])) {
double[][] patternCell = patternGrid[iUV[1]][iUV[0]];
if (patternCell[0].length > 2)
gridIntensity[iUV[1]][iUV[0]] = patternCell[0][2]; // just copy from patternGrid[v][u][0][2]
}
/*
* gridIntensity[iUV[1]][iUV[0]]=localGridContrast( imp, equalizeGreens,
* patternGrid, iUV[0], iUV[1]);
*/
}
}
}
};
}
startAndJoin(threads);
return gridIntensity;
}
/* ======================================================================== */
public double localGridContrast(ImagePlus imp, boolean equalizeGreens, final double[][][][] patternGrid, int u,
int v) {
if (!isCellDefined(patternGrid, u, v))
return 0.0;
if (!isCellDefined(patternGrid, u + 1, v))
return 0.0;
if (!isCellDefined(patternGrid, u, v + 1))
return 0.0;
if (!isCellDefined(patternGrid, u - 1, v))
return 0.0;
if (!isCellDefined(patternGrid, u, v - 1))
return 0.0;
double[][] deltas = {
{ 0.25 * (patternGrid[v + 1][u][0][0] - patternGrid[v - 1][u][0][0]),
0.25 * (patternGrid[v + 1][u][0][1] - patternGrid[v - 1][u][0][1]) },
{ 0.25 * (patternGrid[v][u + 1][0][0] - patternGrid[v][u - 1][0][0]),
0.25 * (patternGrid[v][u + 1][0][1] - patternGrid[v][u - 1][0][1]) } };
double delta = Math.sqrt(0.5 * (deltas[0][0] * deltas[0][0] + deltas[0][1] * deltas[0][1]
+ deltas[1][0] * deltas[1][0] + deltas[1][1] * deltas[1][1]));
int range = (int) Math.round(0.25 * delta); // center of the white/black;
int[][] iDeltas = { { (int) Math.round(deltas[0][0]), (int) Math.round(deltas[0][1]) },
{ (int) Math.round(deltas[1][0]), (int) Math.round(deltas[1][1]) } };
int[][] centersOnBayer4 = { { iDeltas[0][0], iDeltas[0][1] }, { -iDeltas[0][0], -iDeltas[0][1] },
{ iDeltas[1][0], iDeltas[1][1] }, { -iDeltas[1][0], -iDeltas[1][1] } };
double diff = 0.0;
double sum = 0.0;
int maxDxy = 0;
for (int n = 0; n < 4; n++)
for (int i = 0; i < 2; i++)
if (centersOnBayer4[n][i] > maxDxy)
maxDxy = centersOnBayer4[n][i];
int size = 2 * (maxDxy + range + 1); // this will include all needed pixels
// size+=2;
int hSize = size / 2;
boolean debug = false; // ((u==30) && (v==30));
Rectangle centerCross = correlationSelection(patternGrid[v][u][0], // initial coordinates of the pattern cross
// point
size); // distortionParameters.correlationSize);
// Rectangle thisSel=new Rectangle(centerCross.x,centerCross.y,2*size,2*size);
// // "2" - sensor pixels, befor split to components
double[][] input_bayer = splitBayer(imp, centerCross, equalizeGreens);
if (debug)
ShowDoubleFloatArrays.showArrays(input_bayer, size, size, true, imp.getTitle() + "-bayer");
double[] bayer4 = input_bayer[4];
for (int dv = -range; dv <= range; dv++)
for (int du = -range; du <= range; du++) {
int[] indices = new int[4];
for (int n = 0; n < 4; n++)
indices[n] = size * (centersOnBayer4[n][1] + dv + hSize) + (centersOnBayer4[n][0] + du + hSize);
if ((indices[0] > bayer4.length) || (indices[1] > bayer4.length) || (indices[2] > bayer4.length)
|| (indices[3] > bayer4.length) || (indices[0] < 0) || (indices[1] < 0) || (indices[2] < 0)
|| (indices[3] < 0) || debug) {
System.out.println(
"centersOnBayer4[0]={" + centersOnBayer4[0][0] + ", " + centersOnBayer4[0][1] + "}");
System.out.println(
"centersOnBayer4[1]={" + centersOnBayer4[1][0] + ", " + centersOnBayer4[1][1] + "}");
System.out.println(
"centersOnBayer4[2]={" + centersOnBayer4[2][0] + ", " + centersOnBayer4[2][1] + "}");
System.out.println(
"centersOnBayer4[3]={" + centersOnBayer4[3][0] + ", " + centersOnBayer4[3][1] + "}");
System.out.println("range=" + range);
System.out.println("dv=" + dv + " du=" + du);
System.out.println("maxDxy=" + maxDxy + " size=" + size + " hSize=" + hSize);
System.out.println("indices=={" + indices[0] + ", " + indices[1] + ", " + indices[2] + ", "
+ indices[3] + "}, bayer4.length=" + bayer4.length);
}
sum += bayer4[indices[0]] + bayer4[indices[1]] + bayer4[indices[2]] + bayer4[indices[3]];
diff += bayer4[indices[0]] + bayer4[indices[1]] - bayer4[indices[2]] - bayer4[indices[3]];
}
if (((u ^ v) & 1) != 0)
diff = -diff;
if (sum == 0.0)
return 0.0;
return diff / sum;
}
/* ======================================================================== */
public double averageGridPeriod(double[][][][] patternGrid) {
int n = 0;
double sum = 0.0;
int[] iUV = new int[2];
int[] iUV1 = new int[2];
int[][] dirs = { { 0, 1 }, { 1, 0 } };
double dx, dy;
for (iUV[1] = 0; iUV[1] < patternGrid.length - 1; iUV[1]++)
for (iUV[0] = 0; iUV[0] < patternGrid[0].length - 1; iUV[0]++)
if (isCellDefined(patternGrid, iUV)) {
for (int dir = 0; dir < dirs.length; dir++) {
iUV1[0] = iUV[0] + dirs[dir][0];
iUV1[1] = iUV[1] + dirs[dir][1];
if (isCellDefined(patternGrid, iUV1)) {
// dx=patternGrid[iUV1[1]][iUV1[0]][0][0]-patternGrid[iUV1[1]][iUV[0]][0][0]; //
// old bug, skewed period!
// dy=patternGrid[iUV1[1]][iUV1[0]][0][1]-patternGrid[iUV1[1]][iUV[0]][0][1]; //
// old bug, skewed period!
dx = patternGrid[iUV1[1]][iUV1[0]][0][0] - patternGrid[iUV[1]][iUV[0]][0][0];
dy = patternGrid[iUV1[1]][iUV1[0]][0][1] - patternGrid[iUV[1]][iUV[0]][0][1];
sum += dx * dx + dy * dy;
n++;
}
}
}
if (n > 0)
sum /= n;
return Math.sqrt(sum);
}
/* ======================================================================== */
public double[] averageGridPeriods( // min,max for u,v
double[][][][] patternGrid) {
double[] result = { Double.NaN, Double.NaN };
// int n=0;
double[] sum = { 0.0, 0.0 };
int[] numSamples = { 0, 0 };
int[] iUV = new int[2];
int[] iUV1 = new int[2];
int[][] dirs = { { 0, 1 }, { 1, 0 } };
double dx, dy;
for (iUV[1] = 0; iUV[1] < patternGrid.length - 1; iUV[1]++)
for (iUV[0] = 0; iUV[0] < patternGrid[0].length - 1; iUV[0]++)
if (isCellDefined(patternGrid, iUV)) {
for (int dir = 0; dir < dirs.length; dir++) {
iUV1[0] = iUV[0] + dirs[dir][0];
iUV1[1] = iUV[1] + dirs[dir][1];
if (isCellDefined(patternGrid, iUV1)) {
dx = patternGrid[iUV1[1]][iUV1[0]][0][0] - patternGrid[iUV[1]][iUV[0]][0][0];
dy = patternGrid[iUV1[1]][iUV1[0]][0][1] - patternGrid[iUV[1]][iUV[0]][0][1];
sum[dir] += dx * dx + dy * dy;
numSamples[dir]++;
}
}
}
for (int dir = 0; dir < dirs.length; dir++) {
if (numSamples[dir] > 0)
result[dir] = Math.sqrt(sum[dir] / numSamples[dir]);
}
if (result[0] > result[1]) {
double tmp = result[0];
result[0] = result[1];
result[1] = tmp;
}
return result;
}
/* ======================================================================== */
public double[] calcFlatFieldForGrid(double[][] gridIntensity, double[][][][] patternGrid, int sWidth,
int sHeight) {
int width = patternGrid[0].length;
int height = patternGrid.length;
int[][] uvInc = { { 0, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } }; // four corners as u,v pair
int[][] cycles = { // counter-clockwise corners bounding the area (only orthogonal sides?)
{ 1, 0, 2 }, { 2, 3, 1 }, { 0, 2, 3 }, { 3, 1, 0 } };
double[] fffg = new double[sWidth * sHeight];
int[] fffgNum = new int[sWidth * sHeight];
for (int i = 0; i < fffg.length; i++) {
fffg[i] = 0.0;
fffgNum[i] = 0;
}
int[] iUV = new int[2];
for (int v = 0; v < (height - 1); v++)
for (int u = 0; u < (width - 1); u++) {
double[][] cornerXY = new double[4][];
for (int i = 0; i < uvInc.length; i++) {
iUV[0] = u + uvInc[i][0];
iUV[1] = v + uvInc[i][1];
if (isCellDefined(patternGrid, iUV)) {
cornerXY[i] = new double[3];
cornerXY[i][0] = patternGrid[iUV[1]][iUV[0]][0][0];
cornerXY[i][1] = patternGrid[iUV[1]][iUV[0]][0][1];
cornerXY[i][2] = gridIntensity[iUV[1]][iUV[0]];
} else
cornerXY[i] = null;
}
boolean[] cycleFits = new boolean[cycles.length];
for (int i = 0; i < cycles.length; i++) {
cycleFits[i] = true;
for (int j = 0; j < cycles[i].length; j++)
if (cornerXY[cycles[i][j]] == null) {
cycleFits[i] = false;
break;
}
}
if (cycleFits[0] && cycleFits[1]) { // remove overlaps
cycleFits[2] = false;
cycleFits[3] = false;
}
boolean minMaxUndefined = true;
double minX = 0, maxX = 0, minY = 0, maxY = 0;
// find bounding rectangle;
for (int nCycle = 0; nCycle < cycles.length; nCycle++)
if (cycleFits[nCycle]) {
int[] cycle = cycles[nCycle];
for (int corner = 0; corner < cycle.length; corner++) {
if (minMaxUndefined || (minX > cornerXY[cycle[corner]][0]))
minX = cornerXY[cycle[corner]][0];
if (minMaxUndefined || (maxX < cornerXY[cycle[corner]][0]))
maxX = cornerXY[cycle[corner]][0];
if (minMaxUndefined || (minY > cornerXY[cycle[corner]][1]))
minY = cornerXY[cycle[corner]][1];
if (minMaxUndefined || (maxY < cornerXY[cycle[corner]][1]))
maxY = cornerXY[cycle[corner]][1];
minMaxUndefined = false;
}
}
int iMinX = (int) Math.floor(minX);
int iMinY = (int) Math.floor(minY);
int iMaxX = (int) Math.ceil(maxX);
int iMaxY = (int) Math.ceil(maxY);
if (iMinX < 0)
iMinX = 0;
if (iMinY < 0)
iMinY = 0;
if (iMaxX >= sWidth)
iMaxX = sWidth - 1;
if (iMaxY >= sHeight)
iMaxY = sHeight - 1;
double[] originXY = new double[2];
double[] endXY = new double[2];
for (int idY = iMinY; idY <= iMaxY; idY++) {
double pY = idY; // in sensor pixels
for (int idX = iMinX; idX <= iMaxX; idX++) {
double pX = idX; // in sensor pixels
// scan allowed triangles, usually 2
for (int nCycle = 0; nCycle < cycles.length; nCycle++)
if (cycleFits[nCycle]) {
int[] cycle = cycles[nCycle];
// is this point inside?
boolean inside = true;
for (int nEdge = 0; nEdge < cycle.length; nEdge++) {
int nextNEdge = (nEdge == (cycle.length - 1)) ? 0 : (nEdge + 1);
originXY[0] = patternGrid[v + uvInc[cycle[nEdge]][1]][u
+ uvInc[cycle[nEdge]][0]][0][0];
originXY[1] = patternGrid[v + uvInc[cycle[nEdge]][1]][u
+ uvInc[cycle[nEdge]][0]][0][1];
endXY[0] = patternGrid[v + uvInc[cycle[nextNEdge]][1]][u
+ uvInc[cycle[nextNEdge]][0]][0][0];
endXY[1] = patternGrid[v + uvInc[cycle[nextNEdge]][1]][u
+ uvInc[cycle[nextNEdge]][0]][0][1];
if (((pX - originXY[0]) * (endXY[1] - originXY[1])
- (pY - originXY[1]) * (endXY[0] - originXY[0])) < 0.0) {
inside = false;
break;
}
}
if (!inside)
continue; // point is outside of the interpolation area, try next triangle (if any)
/*
* interpolate: 1. taking cycles[0] as origin and two (non co-linear) edge
* vectors - V1:from 0 to 1 and V2 from 1 to 2 find a1 and a2 so that vector V
* (from 0 to pXY) = a1*V1+ a2*V2 2. if F0 is the value of the interpolated
* function at cycles[0], F1 and F2 - at cycles[1] and cycles2 then
* F=F0+(F1-F0)*a1 +(F2-F1)*a2
*/
double[] XY0 = { patternGrid[v + uvInc[cycle[0]][1]][u + uvInc[cycle[0]][0]][0][0],
patternGrid[v + uvInc[cycle[0]][1]][u + uvInc[cycle[0]][0]][0][1] };
double[] XY1 = { patternGrid[v + uvInc[cycle[1]][1]][u + uvInc[cycle[1]][0]][0][0],
patternGrid[v + uvInc[cycle[1]][1]][u + uvInc[cycle[1]][0]][0][1] };
double[] XY2 = { patternGrid[v + uvInc[cycle[2]][1]][u + uvInc[cycle[2]][0]][0][0],
patternGrid[v + uvInc[cycle[2]][1]][u + uvInc[cycle[2]][0]][0][1] };
double[] V = { pX - XY0[0], pY - XY0[1] };
double[][] M = { { XY1[0] - XY0[0], XY2[0] - XY1[0] },
{ XY1[1] - XY0[1], XY2[1] - XY1[1] } };
double det = M[0][0] * M[1][1] - M[1][0] * M[0][1];
double[][] MInverse = { { M[1][1] / det, -M[0][1] / det },
{ -M[1][0] / det, M[0][0] / det } };
double[] a12 = { MInverse[0][0] * V[0] + MInverse[0][1] * V[1],
MInverse[1][0] * V[0] + MInverse[1][1] * V[1] };
int pCorrIndex = idY * sWidth + idX;
// some points may be accumulated multiple times - thisPCorr[3] will take care
// of this
if (this.debugLevel > 3) {
System.out.println("XY0=" + IJ.d2s(XY0[0], 3) + ":" + IJ.d2s(XY0[1], 3));
System.out.println("XY1=" + IJ.d2s(XY1[0], 3) + ":" + IJ.d2s(XY1[1], 3));
System.out.println("XY2=" + IJ.d2s(XY2[0], 3) + ":" + IJ.d2s(XY2[1], 3));
System.out.println("M00=" + IJ.d2s(M[0][0], 3) + " M01=" + IJ.d2s(M[0][1], 3));
System.out.println("M10=" + IJ.d2s(M[1][0], 3) + " M11=" + IJ.d2s(M[1][1], 3));
System.out.println("MInverse00=" + IJ.d2s(MInverse[0][0], 5) + " MInverse01="
+ IJ.d2s(MInverse[0][1], 5));
System.out.println("MInverse10=" + IJ.d2s(MInverse[1][0], 5) + " MInverse11="
+ IJ.d2s(MInverse[1][1], 5));
System.out.println("a12=" + IJ.d2s(a12[0], 3) + ":" + IJ.d2s(a12[1], 3));
System.out.println("gridIntensity[v+uvInc[cycle[0]][1]][u+uvInc[cycle[0]][0]]="
+ IJ.d2s(gridIntensity[v + uvInc[cycle[0]][1]][u + uvInc[cycle[0]][0]], 3));
System.out.println("gridIntensity[v+uvInc[cycle[1]][1]][u+uvInc[cycle[1]][0]]="
+ IJ.d2s(gridIntensity[v + uvInc[cycle[1]][1]][u + uvInc[cycle[1]][0]], 3));
System.out.println("gridIntensity[v+uvInc[cycle[2]][1]][u+uvInc[cycle[2]][0]]="
+ IJ.d2s(gridIntensity[v + uvInc[cycle[2]][1]][u + uvInc[cycle[2]][0]], 3));
}
double val = gridIntensity[v + uvInc[cycle[0]][1]][u + uvInc[cycle[0]][0]]
+ (gridIntensity[v + uvInc[cycle[1]][1]][u + uvInc[cycle[1]][0]]
- gridIntensity[v + uvInc[cycle[0]][1]][u + uvInc[cycle[0]][0]])
* a12[0]
+ (gridIntensity[v + uvInc[cycle[2]][1]][u + uvInc[cycle[2]][0]]
- gridIntensity[v + uvInc[cycle[1]][1]][u + uvInc[cycle[1]][0]])
* a12[1];
if (this.debugLevel > 3) {
System.out.println("val=" + IJ.d2s(val, 3));
}
fffg[pCorrIndex] += val;// error in /data/focus/grid3d/center/1317924548_967543-00.tiff
// OOB: 5019002
fffgNum[pCorrIndex] += 1;
}
} // idX
// use same order in calculations, make sure no gaps
} // idY
} // finished image
for (int i = 0; i < fffg.length; i++)
if (fffgNum[i] > 0) {
fffg[i] /= fffgNum[i];
}
return fffg;
}
/* ======================================================================== */
/**
* Extrapolates flat-field correction
*
* @param data [nPixels] data to extrapolate
* @param sWidth data width
* @param preShrink shrink the non-zero data by this number of pixels before
* extrapolating
* @param expand expand the (pre-shrank) data by up to this number of pixels
* @param sigma when fitting plane through new point use Gaussian weight
* function for the neighbors (normalized to non-decimated
* points)
* @param ksigma Process pixels in a square with the side 2*sigma*ksigma
*/
// TODO: Use threads
public boolean extrapolatePatternFlatFieldCorrection(final double[] data, // fieldXY,
final int sWidth, final int preShrink, final int expand, final double sigma, final double ksigma,
int threadsMax, // 100; // testing multi-threading, limit maximal number of threads
boolean updateStatus) {
int dbgThreshold = 1;
final int length = data.length;
final int sHeight = length / sWidth;
// create mask
final boolean[] fMask = new boolean[data.length];
for (int i = 0; i < length; i++)
fMask[i] = data[i] > 0.0;
final int len = (int) Math.ceil(sigma * ksigma);
final double[] gaussian = new double[len + 1];
double k = 0.5 / sigma / sigma;
for (int i = 0; i <= len; i++)
gaussian[i] = Math.exp(-i * i * k);
int[][] dirs = { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } }; // order matters
final List extList = new ArrayList(1000);
Integer Index, Index2;
extList.clear();
// create initial wave
if (this.debugLevel > 2)
System.out.println("extrapolatePatternFlatFieldCorrection() sWidth=" + sWidth + " sHeight=" + sHeight);
for (int iy = 0; iy < sHeight; iy++)
for (int ix = 0; ix < sWidth; ix++) {
Index = iy * sWidth + ix;
if (fMask[Index]) {
int numNew = 0;
for (int dir = 0; dir < dirs.length; dir++) {
int ix1 = ix + dirs[dir][0];
int iy1 = iy + dirs[dir][1];
if ((ix1 >= 0) && (iy1 >= 0) && (ix1 < sWidth) && (iy1 < sHeight)) {
if (!fMask[iy1 * sWidth + ix1])
numNew++;
}
if (numNew > 0)
extList.add(Index); // neighbor will have non-singular matrix
}
}
}
// now shrink
// unmask current wave
for (int i = extList.size() - 1; i >= 0; i--)
fMask[extList.get(i)] = false;
if (extList.size() == 0)
return false; // no points
for (int nShrink = 0; nShrink < preShrink; nShrink++) {
int size = extList.size();
if (size == 0)
return false; // no points
// wave step, unmasking
for (int i = 0; i < size; i++) {
Index = extList.get(0);
extList.remove(0);
int iy = Index / sWidth;
int ix = Index % sWidth;
for (int dir = 0; dir < dirs.length; dir++) {
int ix1 = ix + dirs[dir][0];
int iy1 = iy + dirs[dir][1];
if ((ix1 >= 0) && (iy1 >= 0) && (ix1 < sWidth) && (iy1 < sHeight)) {
Index = iy1 * sWidth + ix1;
if (fMask[Index]) {
extList.add(Index);
fMask[Index] = false; // restore later?
}
}
}
}
}
// restore mask on the front
for (int i = extList.size() - 1; i >= 0; i--)
fMask[extList.get(i)] = true;
if (this.debugLevel > dbgThreshold + 1) {
for (int i = 0; i < length; i++)
if (!fMask[i])
data[i] = 0.0;
ShowDoubleFloatArrays.showArrays(data, sWidth, sHeight, "shrank");
}
// repeat with the wave until there is place to move, but not more than "expand"
// steps
final Thread[] threads = newThreadArray(threadsMax);
final AtomicInteger pixInWaveNum = new AtomicInteger();
int[] dirs2 = new int[2];
for (int n = 0; (n < expand) && (extList.size() > 0); n++) {
if (updateStatus)
IJ.showStatus("Expanding, step=" + (n + 1) + " (of " + expand + "), extList.size()=" + extList.size());
if (this.debugLevel > 2)
System.out.println("Expanding, step=" + n + ", extList.size()=" + extList.size());
// move wave front 1 pixel hor/vert
for (int i = extList.size(); i > 0; i--) { // repeat current size times
Index = extList.get(0);
extList.remove(0);
int iy = Index / sWidth;
int ix = Index % sWidth;
for (int dir = 0; dir < dirs.length; dir++) {
int ix1 = ix + dirs[dir][0];
int iy1 = iy + dirs[dir][1];
if ((ix1 >= 0) && (iy1 >= 0) && (ix1 < sWidth) && (iy1 < sHeight)) {
Index = iy1 * sWidth + ix1;
if (!fMask[Index]) {
// verify it has neighbors in the perpendicular direction to dir
dirs2[0] = (dir + 2) & 3;
dirs2[1] = dirs2[0] ^ 1;
for (int dir2 = 0; dir2 < dirs2.length; dir2++) {
int ix2 = ix + dirs[dirs2[dir2]][0]; // from the old, not the new point!
int iy2 = iy + dirs[dirs2[dir2]][1];
if ((ix2 >= 0) && (iy2 >= 0) && (ix2 < sWidth) && (iy2 < sHeight)) {
Index2 = iy2 * sWidth + ix2;
if (fMask[Index2]) { // has orthogonal neighbor, OK to add
extList.add(Index);
fMask[Index] = true; // remove later
break;
}
}
}
}
}
}
}
// now un-mask the pixels in new list new
for (int i = 0; i < extList.size(); i++) {
Index = extList.get(i);
fMask[Index] = false; // now mask is only set for known pixels
}
// Calculate values (extrapolate) for the pixels in the list
/*
* Err = sum (W(x,y)*(f(x,y)-F0-Ax*(x-X0)-Ay*(y-Y0))^2)= sum
* (Wxy*(Fxy^2+F0^2+Ax^2*(x-X0)^2+Ay^2*(y-Y0)^2 -2*Fxy*F0 -2*Fxy*Ax*(x-X0) -
* 2*Fxy*Ay*(y-Y0) +2*F0*Ax*(x-X0) + 2*F0*Ay*(y-Y0) +2*Ax*(x-X0)*Ay*(y-Y0))
* (1)0=dErr/dF0= 2*sum (Wxy*(F0-Fxy+Ax*(x-X0)+Ay(y-Y0))) (2)0=dErr/dAx= 2*sum
* (Wxy*(Ax*(x-X0)^2-Fxy*(x-X0) +F0*(x-X0)+Ay*(x-x0)*(y-Y0))) (3)0=dErr/dAy=
* 2*sum (Wxy*(Ay*(y-y0)^2-Fxy*(y-Y0) +F0*(y-Y0)+Ax*(x-x0)*(y-Y0)))
*
* S0 = sum(Wxy) SF= sum(Wxy*Fxy) SX= sum(Wxy*(x-X0) SY= sum(Wxy*(y-Y0) SFX=
* sum(Wxy*Fxy*(x-X0) SFY= sum(Wxy*Fxy*(y-Y0) SX2= sum(Wxy*(x-X0)^2 SY2=
* sum(Wxy*(y-Y0)^2 SXY= sum(Wxy*(x-X0)*(y-Y0)
*
* (1) F0*S0 - SF + Ax*SX +Ay*Sy = 0 (2) Ax*SX2-SFX+F0*SX+Ay*SXY = 0 (3) Ay*Sy2
* -SFY + F0*SY +Ax*SXY = 0
*
* (1) F0*S0 + Ax*SX +Ay*SY = SF (2) Ax*SX2+F0*SX+Ay*SXY = SFX (3) Ay*Sy2 +
* F0*SY +Ax*SXY = SFY
*
*
* | F0 | V= | Ax | | Ay |
*
* | SF | B = | SFX | | SFY |
*
* | S0 SX SY | M = | SX SX2 SXY | | SY SXY SY2 |
*
* M * V = B
*/
pixInWaveNum.set(0);
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() {
@Override
public void run() {
// for (int i =0;i= sHeight)
iYmax = sHeight - 1;
int iXmin = ix - len;
if (iXmin < 0)
iXmin = 0;
int iXmax = ix + len;
if (iXmax >= sWidth)
iXmax = sWidth - 1;
for (int iy1 = iYmin; iy1 <= iYmax; iy1++)
for (int ix1 = iXmin; ix1 <= iXmax; ix1++) {
int ind = ix1 + iy1 * sWidth;
if (fMask[ind]) {
double w = gaussian[(iy1 >= iy) ? (iy1 - iy) : (iy - iy1)]
* gaussian[(ix1 >= ix) ? (ix1 - ix) : (ix - ix1)];
S0 += w;
SF += w * data[ind];
SX += w * (ix1 - ix);
SY += w * (iy1 - iy);
SFX += w * data[ind] * (ix1 - ix);
SFY += w * data[ind] * (iy1 - iy);
SX2 += w * (ix1 - ix) * (ix1 - ix);
SY2 += w * (iy1 - iy) * (iy1 - iy);
SXY += w * (ix1 - ix) * (iy1 - iy);
}
}
double[][] aB = { { SF }, { SFX }, { SFY } };
double[][] aM = { { S0, SX, SY }, { SX, SX2, SXY }, { SY, SXY, SY2 } };
Matrix B = new Matrix(aB);
Matrix M = new Matrix(aM);
if (!(new LUDecomposition(M)).isNonsingular() && (S0 != 0.0)) {
data[Indx] = SF / S0;
} else {
Matrix V = M.solve(B); // sometimes singular
data[Indx] = V.get(0, 0);
}
}
}
};
}
startAndJoin(threads);
// set mask again for the new calculated layer of pixels
for (int i = 0; i < extList.size(); i++) {
Index = extList.get(i);
fMask[Index] = true;
}
IJ.showProgress(n + 1, expand);
}
IJ.showProgress(1.0);
return true;
}
/* ======================================================================== */
private double[][] calcNeibLocsWeights(DistortionParameters distortionParameters, boolean useNeib) {
double[][] locsNeib = { { 0.0, 0.0, 1.0 } };
if (!useNeib)
return locsNeib;
locsNeib = new double[9][3];
double[][] dirs = { { 0.0, 0.0 }, { 1.0, 0.0 }, { 0.0, 1.0 }, { -1.0, 0.0 }, { 0.0, -1.0 }, { 1.0, 1.0 },
{ 1.0, -1.0 }, { -1.0, 1.0 }, { -1.0, -1.0 } };
int i;
locsNeib[0][2] = 1.0 - distortionParameters.averageOrthoWeight - distortionParameters.averageOrthoWeight;
for (i = 0; i < 4; i++) {
locsNeib[i + 1][2] = 0.25 * distortionParameters.averageOrthoWeight;
locsNeib[i + 5][2] = 0.25 * distortionParameters.averageDiagWeight;
}
double k = 1.0;
for (i = 0; i < 9; i++) {
if (i > 0)
k = distortionParameters.averageOrthoDist;
if (i > 4)
k = distortionParameters.averageDiagDist;
locsNeib[i][0] = k * dirs[i][0];
locsNeib[i][1] = k * dirs[i][1];
}
return locsNeib;
}
/* ======================================================================== */
public void zeroNaNContrast() {
for (double[][][] row : this.PATTERN_GRID) {
for (double[][] node : row) {
if ((node != null) && (node.length > 0) && (node[0] != null) && (node[0].length > 2)) {
if (Double.isNaN(node[0][2]))
node[0][2] = 0.0;
}
}
}
}
public double[][][][] recalculateWaveVectors(
// double[][][][] patternGrid,
final boolean updateStatus, final int debug_level) {// debug level used inside loops
// double[][][][] patternGrid=this.PATTERN_GRID;
int i;
int[] iuv = new int[2];
final int[][] directionsUV8 = { { 1, 0 }, { 0, 1 }, { -1, 0 }, { 0, -1 }, { 1, 1 }, { -1, 1 }, { -1, -1 },
{ 1, -1 } }; // first 8 should be the same as in directionsUV
final int[] directionsBits8 = { 1, 4, 1, 4, 2, 8, 2, 8 }; // should match directionsUV8
int neibBits;
int dir;
int[] iUV = new int[2];
double[][][] neibors = new double[8][][]; // uv and xy vectors to 8 neibors (some may be null
double[][] thisCell;
double[][] otherCell;
int was_debug_level = debugLevel;
debugLevel = debug_level;
if (debugLevel > 1)
System.out.println("Recalculating wave vectors from coordinates...");
for (iuv[1] = 0; iuv[1] < this.PATTERN_GRID.length; iuv[1]++)
for (iuv[0] = 0; iuv[0] < this.PATTERN_GRID[0].length; iuv[0]++)
if (isCellValid(this.PATTERN_GRID, iuv)) {
if (debugLevel > 2)
System.out.println("<---= iuv= " + iuv[0] + ", " + iuv[1]);
thisCell = this.PATTERN_GRID[iuv[1]][iuv[0]];
neibBits = 0;
for (dir = 0; dir < directionsUV8.length; dir++) {
neibors[dir] = null;
iUV[0] = iuv[0] + directionsUV8[dir][0];
iUV[1] = iuv[1] + directionsUV8[dir][1];
if ((iUV[0] < 0) || (iUV[1] < 0) || (iUV[0] >= this.PATTERN_GRID[0].length)
|| (iUV[1] >= this.PATTERN_GRID.length))
continue; // don't fit into UV grid
if (isCellValid(this.PATTERN_GRID, iUV)) {
neibors[dir] = new double[2][2];
otherCell = this.PATTERN_GRID[iUV[1]][iUV[0]];
neibors[dir][0][0] = 0.5 * directionsUV8[dir][0]; // u
neibors[dir][0][1] = 0.5 * directionsUV8[dir][1]; // v
neibors[dir][1][0] = otherCell[0][0] - thisCell[0][0]; // x
neibors[dir][1][1] = otherCell[0][1] - thisCell[0][1]; // y
neibBits |= directionsBits8[dir];
}
}
i = Integer.bitCount(neibBits);
if (debugLevel > 2)
System.out.println("neibBits=" + neibBits + ", number of bits= " + i);
if (i > 1) {
double[][] wv = waveVectorsFromNeib(neibors);
setPatternGridCell(this.PATTERN_GRID, iuv, null, // XY already set
wv[0], wv[1]);
if (debugLevel > 2)
System.out.println("==+> number of bits:" + i + " vw00=" + IJ.d2s(wv[0][0], 5) + " vw01="
+ IJ.d2s(wv[0][1], 5) + " vw10=" + IJ.d2s(wv[1][0], 5) + " vw11="
+ IJ.d2s(wv[1][1], 5));
// wv= WaveVectorsFromNeib(neibors);
//
// vectors: [num_vector][0][0] - U
// [num_vector][0][1] - V
// [num_vector][1][0] - X
// [num_vector][1][1] - Y
// [num_vector] == null - skip
//
}
}
debugLevel = was_debug_level;
return this.PATTERN_GRID;
}
/* ======================================================================== */
private double[][] waveVectorsFromNeib(double[][][] vectors) {
/*
* vectors: [num_vector][0][0] - U [num_vector][0][1] - V [num_vector][1][0] - X
* [num_vector][1][1] - Y [num_vector] == null - skip minimizing sum of squared
* errors.
*
* Ui=Xi*Wv00+Yi*Wv01 Vi=Xi*Wv10+Yi*Wv11
*
* sum(Xi^2) *Wv00 +sum(Xi*Yi)*Wv01- sum(Xi*Ui) =0 sum(Xi*Yi)*Wv00 +sum(Yi^2)
* *Wv01- sum(Yi*Ui) =0
*
*
* sum(Xi^2) *Wv10 +sum(Xi*Yi)*Wv11- sum(Xi*Vi) =0 sum(Xi*Yi)*Wv10 +sum(Yi^2)
* *Wv11- sum(Yi*Vi) =0
*
* S= | sum(Xi^2) sum(Xi*Yi) | | sum(Xi*Yi) sum(Yi^2) |
*
* SU= | sum(Xi*Ui) | | sum(Yi*Ui) |
*
* SV= | sum(Xi*Vi) | | sum(Yi*Vi) |
*
* Wv0=| Wv00 | | Wv01 |
*
* Wv1=| Wv10 | | Wv11 |
*
* S * Wv0 = SU S * Wv1 = SV
*
* Wv0 = inv(S) * SU Wv1 = inv(S) * SV
*
*/
int i;
double[][] S = { { 0.0, 0.0 }, { 0.0, 0.0 } };
double[] SU = { 0.0, 0.0 };
double[] SV = { 0.0, 0.0 };
double[][] WV = new double[2][];
for (i = 0; i < vectors.length; i++)
if (vectors[i] != null) {
// if (debugLevel>1) System.out.println("waveVectorsFromNeib: i="+i +": "+
// vectors[i][0][0]+" "+vectors[i][0][1]+" "+vectors[i][1][0]+"
// "+vectors[i][1][1]+" ");
S[0][0] += vectors[i][1][0] * vectors[i][1][0]; // sum(Xi^2)
S[0][1] += vectors[i][1][0] * vectors[i][1][1]; // sum(Xi*Yi)
S[1][1] += vectors[i][1][1] * vectors[i][1][1]; // sum(Yi^2)
SU[0] += vectors[i][1][0] * vectors[i][0][0]; // sum(Xi*Ui)
SU[1] += vectors[i][1][1] * vectors[i][0][0]; // sum(Yi*Ui)
SV[0] += vectors[i][1][0] * vectors[i][0][1]; // sum(Xi*Vi)
SV[1] += vectors[i][1][1] * vectors[i][0][1]; // sum(Yi*Vi)
}
S[1][0] = S[0][1];
// if (debugLevel>1) System.out.println("waveVectorsFromNeib: S00="+S[0][0]+"
// S01="+S[0][1]);
// if (debugLevel>1) System.out.println("waveVectorsFromNeib: S10="+S[1][0]+"
// S11="+S[1][1]);
// if (debugLevel>1) System.out.println("waveVectorsFromNeib: SU0="+SU[0]+ "
// SU1="+SU[1]);
// if (debugLevel>1) System.out.println("waveVectorsFromNeib: SV0="+SV[0]+ "
// SV1="+SV[1]);
S = matrix2x2_invert(S);
// if (debugLevel>1) System.out.println("waveVectorsFromNeib: S00="+S[0][0]+"
// S01="+S[0][1]);
// if (debugLevel>1) System.out.println("waveVectorsFromNeib: S10="+S[1][0]+"
// S11="+S[1][1]);
WV[0] = matrix2x2_mul(S, SU);
WV[1] = matrix2x2_mul(S, SV);
return WV;
}
private void putInWaveList(List list, int[] uv, int dir) {
int l = (Integer.SIZE - 2) / 2;
int mask = (1 << l) - 1;
list.add(new Integer((dir & 3) | ((uv[0] & mask) << 2) | ((uv[1] & mask) << (2 + l))));
}
private int[] getWaveList(List list, int index) {
int l = (Integer.SIZE - 2) / 2;
int mask = (1 << l) - 1;
int d = list.get(index);
int[] result = new int[3];
result[2] = (d & 3);
result[0] = (d >> 2) & mask;
result[1] = (d >> (2 + l)) & mask;
return result;
}
/* ======================================================================== */
// set XY coordinates and (optionally) wave vectors of the pattern grid cell
/*
* cell==null - new cell, not yet defined cell.length==1 - invalid cell
* cell.length>1 - initialized: cell[0]==null - undefined cell[0]!=null -
* defined
*/
private void setPatternGridCell(double[][][][] grid, int[] uv, double[] xy, // may be a 3-element, with contrast
double[] wv1, double[] wv2) {
int i;
initPatternGridCell(grid, uv);
if (xy != null) {
// double [] grid_xy= new double[2];
// for (i=0;i<2;i++) grid_xy[i]=xy[i];
grid[uv[1]][uv[0]][0] = xy.clone(); // grid_xy;
}
if (wv1 != null) {
double[] grid_wv1 = new double[2];
for (i = 0; i < 2; i++)
grid_wv1[i] = wv1[i];
grid[uv[1]][uv[0]][1] = grid_wv1;
}
if (wv2 != null) {
double[] grid_wv2 = new double[2];
for (i = 0; i < 2; i++)
grid_wv2[i] = wv2[i];
grid[uv[1]][uv[0]][2] = grid_wv2;
}
}
private void initPatternGridCell(double[][][][] grid, int[] uv) {
int i;
if (grid[uv[1]][uv[0]] == null) {
double[][] grid_cell = new double[3][];
for (i = 0; i < 3; i++)
grid_cell[i] = null;
grid[uv[1]][uv[0]] = grid_cell;
}
}
// mark the grid cell as invalid
private void invalidatePatternGridCell(double[][][][] grid, int[] uv) {
double[][] cell = new double[1][];
cell[0] = null;
grid[uv[1]][uv[0]] = cell;
}
private void clearPatternGridCell(double[][][][] grid, int[] uv) {
grid[uv[1]][uv[0]] = null;
}
private void markDeletedPatternGridCell(double[][][][] grid, int[] uv) {
if ((grid[uv[1]][uv[0]] != null) && (grid[uv[1]][uv[0]][0] != null)) {
double[] newXYC = new double[4];
for (int i = 0; i < newXYC.length; i++) {
if (i < grid[uv[1]][uv[0]][0].length)
newXYC[i] = grid[uv[1]][uv[0]][0][i];
else
newXYC[i] = Double.NaN;
}
grid[uv[1]][uv[0]][0] = newXYC;
}
// grid[uv[1]][uv[0]]=null;
}
private boolean isCellDeleted(double[][][][] grid, int[] uv) {
return ((uv[1] >= 0) && (uv[0] >= 0) && (uv[1] < grid.length) && (uv[0] < grid[uv[1]].length)
&& (grid[uv[1]][uv[0]] != null) && (grid[uv[1]][uv[0]][0] != null)
&& (grid[uv[1]][uv[0]][0].length > 3));
}
private boolean isCellNew( // modified, for invalid uv will return "not new"
double[][][][] grid, int[] uv) {
// return (uv[1]>=0) && (uv[0]>=0) && (uv[1]= 0) && (uv[0] >= 0) && (uv[1] < grid.length) && (uv[0] < grid[uv[1]].length)
&& ((grid[uv[1]][uv[0]] == null) || (grid[uv[1]][uv[0]].length > 3));
}
private boolean isCellValid(double[][][][] grid, int[] uv) {
if ((uv[1] >= 0) && (uv[0] >= 0) && (uv[1] < grid.length) && (uv[0] < grid[uv[1]].length)) {
double[][] cell = grid[uv[1]][uv[0]];
return ((cell != null) && (cell.length > 1));
}
return false;
}
private boolean isCellDefined(double[][][][] grid, int[] uv) {
return ((uv[1] >= 0) && (uv[0] >= 0) && (uv[1] < grid.length) && (uv[0] < grid[uv[1]].length)
&& (grid[uv[1]][uv[0]] != null) && (grid[uv[1]][uv[0]][0] != null));
}
private boolean isCellDefined(double[][][][] grid, int u, int v) {
return ((v >= 0) && (u >= 0) && (v < grid.length) && (u < grid[v].length) && (grid[v][u] != null)
&& (grid[v][u][0] != null));
}
private boolean isCellDefined(int u, int v) {
return isCellDefined(this.PATTERN_GRID, u, v);
}
private boolean isCellDefined(int[] uv) {
return isCellDefined(this.PATTERN_GRID, uv);
}
// with contrast
private double getCellContrast(double[][][][] grid, int[] uv) {
if ((uv[1] >= 0) && (uv[0] >= 0) && (uv[1] < grid.length) && (uv[0] < grid[uv[1]].length)
&& (grid[uv[1]][uv[0]] != null) && (grid[uv[1]][uv[0]][0] != null)
&& (grid[uv[1]][uv[0]][0].length > 2)) {
return grid[uv[1]][uv[0]][0][2];
} else {
return Double.NaN;
}
}
private double getCellContrast(double[][][][] grid, int u, int v) {
if ((v >= 0) && (u >= 0) && (v < grid.length) && (u < grid[v].length) && (grid[v][u] != null)
&& (grid[v][u][0] != null) && (grid[v][u][0].length > 2)) {
return grid[v][u][0][2];
} else {
return Double.NaN;
}
}
public double getCellContrast(int[] uv) {
return getCellContrast(this.PATTERN_GRID, uv);
}
public double getCellContrast(int u, int v) {
return getCellContrast(this.PATTERN_GRID, u, v);
}
private boolean isCellDefinedC(double[][][][] grid, int[] uv) {
return ((uv[1] >= 0) && (uv[0] >= 0) && (uv[1] < grid.length) && (uv[0] < grid[uv[1]].length)
&& (grid[uv[1]][uv[0]] != null) && (grid[uv[1]][uv[0]][0] != null) && (grid[uv[1]][uv[0]][0].length > 2)
&& !Double.isNaN(grid[uv[1]][uv[0]][0][2]));
}
private boolean isCellDefinedC(double[][][][] grid, int u, int v) {
return ((v >= 0) && (u >= 0) && (v < grid.length) && (u < grid[v].length) && (grid[v][u] != null)
&& (grid[v][u][0] != null) && (grid[v][u][0].length > 2) && !Double.isNaN(grid[v][u][0][2]));
}
public boolean isCellDefinedC(int u, int v) {
return isCellDefinedC(this.PATTERN_GRID, u, v);
}
public boolean isCellDefinedC(int[] uv) {
return isCellDefinedC(this.PATTERN_GRID, uv);
}
/*
* private double [] cellXY(int u, int v){ if (!isCellDefined(u,v)) return null;
* return this.PATTERN_GRID[v][u][0]; } private double [] cellXY(int [] uv){ if
* (!isCellDefined(uv)) return null; return this.PATTERN_GRID[uv[1]][uv[0]][0];
* }
*/
private double[] cellXYC(int u, int v) {
if (!isCellDefined(u, v))
return null;
double[] xyc = { this.PATTERN_GRID[v][u][0][0], this.PATTERN_GRID[v][u][0][1],
(this.PATTERN_GRID[v][u][0].length > 2) ? this.PATTERN_GRID[v][u][0][2]
: ((this.gridContrastBrightness == null) ? 1.0 : this.gridContrastBrightness[0][v][u]) };
return xyc; // this.PATTERN_GRID[uv[1]][uv[0]][0];
}
private double[] cellXYC(int[] uv) {
if (!isCellDefined(uv))
return null;
double[] xyc = { this.PATTERN_GRID[uv[1]][uv[0]][0][0], this.PATTERN_GRID[uv[1]][uv[0]][0][1],
(this.PATTERN_GRID[uv[1]][uv[0]][0].length > 2) ? this.PATTERN_GRID[uv[1]][uv[0]][0][2]
: ((this.gridContrastBrightness == null) ? 1.0
: this.gridContrastBrightness[0][uv[1]][uv[0]]) };
return xyc; // this.PATTERN_GRID[uv[1]][uv[0]][0];
}
public int numDefinedCells() {
return numDefinedCells(this.PATTERN_GRID);
}
public int numDefinedCells(double[][][][] grid) { // calculate/print number of defined nodes in a grid
int[] iUV = new int[2];
int numCells = 0;
for (iUV[1] = 0; iUV[1] < grid.length; iUV[1]++)
for (iUV[0] = 0; iUV[0] < grid[0].length; iUV[0]++)
if (this.isCellDefined(grid, iUV))
numCells++;
return numCells;
}
public int numStrongCells(double min_contrast) {
return numStrongCells(this.PATTERN_GRID, min_contrast);
}
public int numStrongCells(double[][][][] grid, double min_contrast) { // calculate/print number of defined high-contrast nodes in a grid
int[] iUV = new int[2];
int numCells = 0;
for (iUV[1] = 0; iUV[1] < grid.length; iUV[1]++)
for (iUV[0] = 0; iUV[0] < grid[0].length; iUV[0]++)
if (this.getCellContrast(grid, iUV) > min_contrast) {
numCells++;
}
return numCells;
}
public int gridUVWidth() {
return ((this.PATTERN_GRID == null) || (this.PATTERN_GRID.length == 0l) || (this.PATTERN_GRID[0] == null)) ? 0
: this.PATTERN_GRID[0].length;
}
public int gridUVHeight() {
return ((this.PATTERN_GRID == null) || (this.PATTERN_GRID.length == 0l) || (this.PATTERN_GRID[0] == null)) ? 0
: this.PATTERN_GRID.length;
}
/* ======================================================================== */
/**
* returns number of laser pointers matched (or negative error) if
* (this.flatFieldForGrid!=null) it should already be applied !!
*/
public int calculateDistortions(
LwirReaderParameters lwirReaderParameters, // null is OK
MatchSimulatedPattern.DistortionParameters distortionParameters, //
MatchSimulatedPattern.PatternDetectParameters patternDetectParameters,
SimulationPattern.SimulParameters simulParameters,
boolean equalizeGreens,
ImagePlus imp, // image to process has WOI_TOP and possibly WOI_COMPENSATED
LaserPointer laserPointer, // LaserPointer object or null
boolean removeOutOfGridPointers, //
double[][][] hintGrid, // predicted grid array (or null)
double hintGridTolerance, // allowed mismatch (fraction of period) or 0 - orientation only
int threadsMax,
boolean updateStatus,
int global_debug_level, // DEBUG_LEVEL
int debug_level, // debug level used inside loops
boolean noMessageBoxes) {
if (imp == null) {
IJ.showMessage("Error", "There are no images open\nProcess canceled");
return 0;
}
final int debugThreshold = 1;
boolean is_lwir = ((lwirReaderParameters != null) && LwirReaderParameters.is_LWIR(imp));
double min_half_period = (is_lwir ? patternDetectParameters.minGridPeriodLwir
: patternDetectParameters.minGridPeriod) / 2;
double max_half_period = (is_lwir ? patternDetectParameters.maxGridPeriodLwir
: patternDetectParameters.maxGridPeriod) / 2;
int minimal_pattern_cluster = is_lwir ? distortionParameters.minimalPatternClusterLwir
: distortionParameters.minimalPatternCluster;
double threshold_contrast = distortionParameters.threshold_contrast;
int threshold_number = distortionParameters.threshold_number;
boolean invert = false; // is_lwir;
int fft_size = is_lwir ? distortionParameters.FFTSize_lwir : distortionParameters.FFTSize;
long startTime = System.nanoTime();
Roi roi = imp.getRoi();
Rectangle selection;
if (roi == null) {
setWOI(0, 0, imp.getWidth(), imp.getHeight());
selection = new Rectangle(0, 0, imp.getWidth(), imp.getHeight());
} else {
if (roi instanceof PointRoi) {
PointRoi pointRoi = (PointRoi) roi;
Point[] points = pointRoi.getContainedPoints();
int[][] ipoints = new int[points.length][2];
for (int n = 0; n < ipoints.length; n++) {
ipoints[n][0] = points[n].x;
ipoints[n][1] = points[n].y;
}
// as if they a laser pointers
double[][] xyuv = new double[points.length][4];
for (int i = 0; i < ipoints.length; i++) {
xyuv[i][0] = ipoints[i][0];
xyuv[i][1] = ipoints[i][1];
xyuv[i][2] = 0.5;
xyuv[i][3] = 0.5;
}
System.out.println(
"Setting first marker (of " + ipoints.length + ") as pointer 0.5/0.5 - use use multiple?");
System.out.println(
"**** Not yet implemented, use 'Manual hint' command for each of the annotated files ****");
// setPointersXYUV(imp, xyuv);
setWOI(0, 0, imp.getWidth(), imp.getHeight());
selection = new Rectangle(0, 0, imp.getWidth(), imp.getHeight());
// without setting roi to null (maybe setting image roi too?) will not retry if first attempt gets too few points
// there is also ignoring PointRoi later, but alone it outputs "Removing failed node (normally should not happen!), u=4, v=2"
// roi = null;
// imp.setRoi(roi,false);
} else {
setWOI(roi.getBounds());
selection = roi.getBounds();
}
}
this.debugLevel = global_debug_level;
int patternCells = 0+0;
// save initial distortionParameters.correlationMinInitialContrast
double savedCorrelationMinInitialContrast = distortionParameters.correlationMinInitialContrast;
int reTries = 50; // bail out after these attempts
boolean foundGoodCluster = false;
int tryHor = 0, tryVert = 0;
// with distortionParameters.searchOverlap==0.5 (default) step will be FFTSize
// original pixels, so half of the (2xFFTSize) square processed simultaneously
if (distortionParameters.searchOverlap < 0.1)
distortionParameters.searchOverlap = 0.1;
int effectiveWidth = (int) (selection.width * 0.5 / distortionParameters.searchOverlap);
int effectiveHeight = (int) (selection.height * 0.5 / distortionParameters.searchOverlap);
for (int i = fft_size; i < effectiveWidth; i *= 2)
tryHor++;
for (int i = fft_size; i < effectiveHeight; i *= 2)
tryVert++;
int numTries = 1 << (tryHor + tryVert);
boolean[] triedIndices = new boolean[numTries + 1]; // last set - all used
for (int i = 0; i < triedIndices.length; i++)
triedIndices[i] = (i < 3); // mark first 3 as if they are already used
while (reTries > 0) { // outer loop, including refines
while (reTries-- > 0) {
this.PATTERN_GRID = null;
invalidateCalibration();
patternCells = distortions( // calculates matchSimulatedPattern.DIST_ARRAY // invalidates calibration,
// flatFieldForGrid, resets this.PATTERN_GRID
lwirReaderParameters, // null is OK
triedIndices,
distortionParameters, //
patternDetectParameters,
min_half_period,
max_half_period,
simulParameters,
equalizeGreens,
imp,
threadsMax, updateStatus, debug_level, global_debug_level); // debug level
if (global_debug_level > 0)
System.out.println(
"Pattern correlation done at " + IJ.d2s(0.000000001 * (System.nanoTime() - startTime), 3)
+ " found " + patternCells + " cells, reTries left: " + reTries);
if (patternCells > 0) {
foundGoodCluster = true;
break; // new distortions() code - returns non-zero only if passed other tests
}
boolean someLeft = false;
int startScanIndex = 0;
for (startScanIndex = 3; startScanIndex < triedIndices.length; startScanIndex++)
if (!triedIndices[startScanIndex]) {
someLeft = true;
break;
}
if (someLeft) {
if (global_debug_level > 0) {
System.out.println("Initial pattern cluster is too small (" + patternCells
+ "), continuing scanning from index " + startScanIndex);
}
} else { // all tried
if (global_debug_level > 0)
System.out.println("--- Tried all - nothing found --- at "
+ IJ.d2s(0.000000001 * (System.nanoTime() - startTime), 3));
break;
}
}
// restore initial distortionParameters.correlationMinInitialContrast
distortionParameters.correlationMinInitialContrast = savedCorrelationMinInitialContrast;
if (!foundGoodCluster) {
if (global_debug_level > (debugThreshold + 1))
System.out.println(
"calculateDistortions(): Pattern too small, initial cluster had " + patternCells + " cells");
if (global_debug_level > (debugThreshold + 2))
IJ.showMessage("Error", "Pattern too small: " + patternCells);
return distortionParameters.errPatternNotFound;
}
if (!patternOK()) {
if (global_debug_level > (debugThreshold + 1))
System.out.println("Pattern not found");
if (global_debug_level > (debugThreshold + 2))
IJ.showMessage("Error", "Pattern not found");
return distortionParameters.errPatternNotFound;
} else {
if (global_debug_level > 0) // (debugThreshold + 1))
System.out.println("Initial pattern cluster has " + patternCells + " cells");
}
if (global_debug_level > (debugThreshold + 1))
System.out.println(
"Wave vectors recalculated at " + IJ.d2s(0.000000001 * (System.nanoTime() - startTime), 3));
recalculateWaveVectors(updateStatus, debug_level);// debug level used inside loops
ImagePlus imp_eq;
if (distortionParameters.flatFieldCorrection && (this.flatFieldForGrid == null)) // if it is not null it is
// already supposed to be
// applied!
imp_eq = equalizeGridIntensity(
imp,
this.PATTERN_GRID,
distortionParameters, // // makes no sense for LWIR as it normalizes absolute data - actually it does!
equalizeGreens,
global_debug_level,
updateStatus,
threadsMax);
else
imp_eq = imp;
if (distortionParameters.refineCorrelations) {
double maxActualCorr = refineDistortionCorrelation(
lwirReaderParameters, // LwirReaderParameters lwirReaderParameters, // null is OK
distortionParameters, //
patternDetectParameters,
simulParameters,
equalizeGreens,
imp_eq,
0.0, // final double maxCorr, maximal allowed correction, in pixels (0.0) - any
threadsMax,
updateStatus,
debug_level); // debug level
System.out.println(".... maxActualCorr="+maxActualCorr);
if (maxActualCorr <= 0.0) {
// return distortionParameters.errRefineFailed; // 2021
continue;
}
recalculateWaveVectors(
updateStatus,
debug_level);// debug level used inside loops
if (global_debug_level > (debugThreshold + 1))
System.out.println("Second pass over at " + IJ.d2s(0.000000001 * (System.nanoTime() - startTime), 3));
}
// hack gridSize
if ((distortionParameters.gridSize & 1) != 0) {
refineDistortionCorrelation(lwirReaderParameters, // LwirReaderParameters lwirReaderParameters, // null is OK
distortionParameters, //
patternDetectParameters,
simulParameters,
equalizeGreens,
imp_eq,
0.0, // final double maxCorr, maximal allowed correction, in pixels (0.0) - any
threadsMax,
updateStatus,
debug_level); // debug level
recalculateWaveVectors(updateStatus, debug_level);// debug level used inside loops
if (global_debug_level > 0)
System.out.println("Third pass over at " + IJ.d2s(0.000000001 * (System.nanoTime() - startTime), 3));
// hack gridSize
}
// Test number of cells with contrast > (~10.0) and fail if number Of over threshold is below minimal_pattern_cluster?
patternCells = numDefinedCells();
// if ((roi != null) && (patternCells < minimal_pattern_cluster) && !(roi instanceof PointRoi)) {
if (patternCells < minimal_pattern_cluster) { // retry regardless of ROI
if (global_debug_level > (debugThreshold - 1))
System.out.println("Detected pattern is too small: " + patternCells + ", minimum is set to "
+ minimal_pattern_cluster+". Will try again search for a new start point.");
//return distortionParameters.errTooFewCells; // -10
continue;
}
patternCells = numStrongCells(threshold_contrast);
if (patternCells < threshold_number) { // retry regardless of ROI
if (global_debug_level > (debugThreshold - 1))
System.out.println("Detected pattern has too few strong cells (with contrast > "+ threshold_contrast+
"): " + patternCells + ", minimum number is set to "
+ threshold_number+". Will try again search for a new start point.");
//return distortionParameters.errTooFewCells; // -10
continue;
}
break; // enough cells?
} // end of outer loop that includes refines
patternCells = numDefinedCells();
if ((roi != null) && (patternCells < minimal_pattern_cluster) && !(roi instanceof PointRoi)) {
if (global_debug_level > (debugThreshold + 0))
System.out.println("Detected pattern is too small: " + patternCells + ", minimum is set to "
+ minimal_pattern_cluster);
return distortionParameters.errTooFewCells; // -10
}
patternCells = numStrongCells(threshold_contrast);
if ((roi != null) && (patternCells < threshold_number) && !(roi instanceof PointRoi)) {
if (global_debug_level > (debugThreshold + 0))
System.out.println("Detected pattern has too few strong cells (with contrast > "+ threshold_contrast+
"): " + patternCells + ", minimum number is set to "
+ threshold_number+".");
//return distortionParameters.errTooFewCells; // -10
return distortionParameters.errTooFewCells; // -10
}
double[] xy0 = { simulParameters.offsetX, simulParameters.offsetY }; // debug
createUV_INDEX(imp, // or null - just to determine WOI (when getWOI matches image size)
xy0, // add to patterGrid xy, null OK
threadsMax, updateStatus, global_debug_level, // DEBUG_LEVEL
debug_level); // debug level used inside loops
finalizeDistortionsBorder(distortionParameters, //
updateStatus, debug_level);// debug level used inside loops
// Wave vectors are used when calculating PSF
recalculateWaveVectors(updateStatus, debug_level);// debug level used inside loops
int numDifferentFFT = 0;
int maxLn2 = 0;
for (int i = 0; i < getCorrelationSizesUsed().length; i++)
if (getCorrelationSizesUsed()[i]) {
numDifferentFFT++;
maxLn2 = i;
}
if (numDifferentFFT > 1) {
String sizesUsed = "";
for (int i = 0; i < getCorrelationSizesUsed().length; i++)
if (getCorrelationSizesUsed()[i])
sizesUsed += " " + (1 << i);
String msg = "Different correlation FFT sizes used:" + sizesUsed
+ ". You may consider increasing \"Correlation size\" setting to " + (1 << maxLn2)
+ " to reduce artifacts";
if (global_debug_level > (debugThreshold + 0)) {
System.out.println(msg);
if (global_debug_level > (debugThreshold + 1))
IJ.showMessage(msg);
}
} else if (numDifferentFFT > 0) {
String msg = "Single correlation FFT size used: " + (1 << maxLn2);
if (global_debug_level > (debugThreshold + 0))
System.out.println(msg);
}
zeroNaNContrast(); // replace grid NaN with 0
int numPointers = (laserPointer != null) ? laserPointer.laserUVMap.length : 0;
double[][] pointersXY = (numPointers > 0) ? getPointersXYUV(imp, laserPointer) : null;
if (global_debug_level > (debugThreshold + 1)) {
if (pointersXY != null) {
System.out.println("calculateDistortions() numPointers=" + numPointers + " pointersXY.length="
+ pointersXY.length);
for (int ii = 0; ii < pointersXY.length; ii++) {
if (pointersXY[ii] != null) {
System.out.println("calculateDistortions() pointersXY[" + ii + "][0]=" + pointersXY[ii][0]);
System.out.println(" pointersXY[" + ii + "][1]=" + pointersXY[ii][1]);
} else {
System.out.println("calculateDistortions() pointersXY[" + ii + "]=NULL");
}
}
System.out.println(
" hintGrid=" + ((hintGrid == null) ? "NULL" : "not NULL"));
System.out.println(" hintGridTolerance=" + hintGridTolerance);
} else {
System.out.println("pointersXY == null");
}
}
return combineGridCalibration(laserPointer, // LaserPointer object or null
pointersXY, removeOutOfGridPointers, //
hintGrid, // predicted grid array (or null)
hintGridTolerance, // allowed mismatch (fraction of period) or 0 - orientation only
invert, // b/w
global_debug_level, // DEBUG_LEVEL
noMessageBoxes);
}
// ====== end of calculateDistortions()
// ==============================================
/**
* Approximate function z(x,y) as a second degree polynomial
* f(x,y)=A*x^2+B*y^2+C*x*y+D*x+E*y+F data array consists of lines of either 2
* or 3 vectors: 2-element vector x,y variable length vector z (should be the
* same for all samples) optional 1- element vector w (weight of the sample)
*
* returns array of vectors or null each vector (one per each z component) is
* either 6-element- (A,B,C,D,E,F) if quadratic is possible and enabled or
* 3-element - (D,E,F) if linear is possible and quadratic is not possible or
* disbled returns null if not enough data even for the linear approximation
*/
public double[][] approximatePSFQuadratic(double[] psf, // PSF function, square array, nominally positive
double cutoffEnergy, // fraction of energy in the pixels to be used
double cutoffLevel, // minimal level as a fraction of maximal
int minArea, // minimal selected area in pixels
double blurSigma, // optionally blur the selection
double maskCutOff, int debugLevel, // debug level
String title) { // prefix used for debug images
double[] mask = findClusterOnPSF(psf, cutoffEnergy, cutoffLevel, minArea, blurSigma, debugLevel, title);
int numPix = 0;
for (int i = 0; i < mask.length; i++)
if (mask[i] < maskCutOff)
mask[i] = 0.0;
for (int i = 0; i < mask.length; i++)
if (mask[i] > 0.0)
numPix++;
double[][][] data = new double[numPix][3][];
numPix = 0;
int size = (int) Math.sqrt(psf.length);
int hsize = size / 2;
for (int i = 0; i < mask.length; i++)
if (mask[i] > 0.0) {
data[numPix][0] = new double[2];
data[numPix][0][0] = (i % size) - hsize;
data[numPix][0][1] = (i / size) - hsize;
data[numPix][1] = new double[1];
data[numPix][1][0] = psf[i];
data[numPix][2] = new double[1];
data[numPix][2][0] = mask[i];
numPix++;
}
return new PolynomialApproximation(debugLevel).quadraticApproximation(data, false, // use linear approximation
// (instead of quadratic)
1.0E-10, // thershold ratio of matrix determinant to norm for linear approximation (det
// too low - fail)
1.0E-20); // thershold ratio of matrix determinant to norm for quadratic approximation
// (det too low - fail)
}
// ====================================================
public double[] tangetRadialSizes(double ca, // cosine of the center to sample vector
double sa, // sine of the center to sample vector
double[] psf, // PSF function, square array, nominally positive
double cutoffEnergy, // fraction of energy in the pixels to be used
double cutoffLevel, // minimal level as a fraction of maximal
int minArea, // minimal selected area in pixels
double blurSigma, // optionally blur the selection
double maskCutOff, int debugLevel, // debug level
String title) { // prefix used for debug images
double[] mask = findClusterOnPSF(psf, cutoffEnergy, cutoffLevel, minArea, blurSigma, debugLevel, title);
for (int i = 0; i < mask.length; i++)
if (mask[i] < maskCutOff)
mask[i] = 0.0;
int size = (int) Math.sqrt(psf.length);
int hsize = size / 2;
// int nn=0;
double S0 = 0.0, SR = 0.0, ST = 0.0, SR2 = 0.0, ST2 = 0.0; // ,SRT=0.0;
for (int i = 0; i < mask.length; i++)
if (mask[i] > 0.0) {
double x = (i % size) - hsize;
double y = (i / size) - hsize;
double rc = x * ca + y * sa;
double tc = -x * sa + y * ca;
double d = psf[i] * mask[i];
S0 += d;
SR += d * rc;
ST += d * tc;
SR2 += d * rc * rc;
ST2 += d * tc * tc;
// nn++;
}
if (S0 == 0.0)
return null; // make sure it is OK
double[] result = { Math.sqrt(ST2 * S0 - ST * ST) / S0, Math.sqrt(SR2 * S0 - SR * SR) / S0 };
// System.out.println(" mask.length="+mask.length+" nn="+nn+" S0="+S0+"
// SR="+SR+" ST="+ST+" SR2="+SR2+" ST2="+ST2+
// " result={"+result[0]+","+result[1]+"}");
return result;
}
// ====================================================
public double[] x2y2xySizes(double[] psf, // PSF function, square array, nominally positive
double cutoffEnergy, // fraction of energy in the pixels to be used
double cutoffLevel, // minimal level as a fraction of maximal
int minArea, // minimal selected area in pixels
double blurSigma, // optionally blur the selection
double maskCutOff, int debugLevel, // debug level
String title) { // prefix used for debug images
double[] mask = findClusterOnPSF(psf, cutoffEnergy, cutoffLevel, minArea, blurSigma, debugLevel, title);
for (int i = 0; i < mask.length; i++)
if (mask[i] < maskCutOff)
mask[i] = 0.0;
int size = (int) Math.sqrt(psf.length);
int hsize = size / 2;
// int nn=0;
double S0 = 0.0, SX = 0.0, SY = 0.0, SX2 = 0.0, SY2 = 0.0, SXY = 0.0;
for (int i = 0; i < mask.length; i++)
if (mask[i] > 0.0) {
double x = (i % size) - hsize;
double y = (i / size) - hsize;
double d = psf[i] * mask[i];
S0 += d;
SX += d * x;
SY += d * y;
SX2 += d * x * x;
SY2 += d * y * y;
SXY += d * x * y;
// nn++;
}
if (S0 == 0.0)
return null; // make sure it is OK
double[] result = { (SX2 * S0 - SX * SX) / S0 / S0, (SY2 * S0 - SY * SY) / S0 / S0,
(SXY * S0 - SX * SY) / S0 / S0 }; // this may be negative
// System.out.println(" mask.length="+mask.length+" nn="+nn+" S0="+S0+"
// SX="+SX+" SY="+SY+" SX2="+SXR2+" SY2="+SY2+" SXY="+SXY+
// " result={"+result[0]+","+result[1]+","+result[2]+"}");
return result;
}
// ====================================================
public double[] findClusterOnPSF(double[] psf, // PSF function, square array, nominally positive
double cutoffEnergy, // fraction of energy in the pixels to be used
double cutoffLevel, // minimal level as a fraction of maximal
int minArea, // minimal selected area in pixels
double blurSigma, // optionally blur the selection
int debugLevel, // debug level
String title) { // prefix used for debhug images
// int i,j;
int ix, iy, ix1, iy1;
List pixelList = new ArrayList(100);
Integer Index = 0, Index1, IndexMax;
int size = (int) Math.sqrt(psf.length);
// int [][]clusterMap=new int[size][size];
int[][] dirs = { { -1, 0 }, { -1, -1 }, { 0, -1 }, { 1, -1 }, { 1, 0 }, { 1, 1 }, { 0, 1 }, { -1, 1 } };
int len = size * size;
double[] clusterMap = new double[len];
double full_energy = 0.0;
double maxValue = 0;
for (int i = 0; i < len; i++) {
clusterMap[i] = 0.0;
if (psf[i] > 0.0)
full_energy += psf[i];
if (maxValue < psf[i]) {
maxValue = psf[i];
Index = i;
}
}
if (maxValue <= 0.0) {
String msg = "psf array does not contain any positive values";
// IJ.showMessage("Error",msg);
System.out.println("Error " + msg);
throw new IllegalArgumentException(msg);
}
ix = Index % size;
iy = Index / size;
double theresholdLevel = maxValue * cutoffLevel;
double theresholdEnergy = full_energy * cutoffEnergy;
double cluster_energy = 0.0;
int clusterSize = 0;
boolean noNew = true;
if (debugLevel > 1)
System.out.println("findClusterOnPSF(): full_energy=" + full_energy + " theresholdEnergy="
+ theresholdEnergy + " maxValue=" + maxValue + " theresholdLevel=" + theresholdLevel);
if (debugLevel > 1)
System.out.println("findClusterOnPSF(): ix=" + ix + " iy=" + iy);
IndexMax = 0;
int listIndex;
pixelList.clear();
pixelList.add(Index);
clusterSize++;
clusterMap[Index] = 1.0;
cluster_energy += psf[Index];
noNew = true;
while ((pixelList.size() > 0) && ((clusterSize < minArea) || (cluster_energy < theresholdEnergy))) { // will
// break
// from
// the
// loop
// if
// (psf[Index]
// 0) || (dirs[j][1] >= 0)) && ((iy < (size - 1)) || (dirs[j][1] <= 0))) {
ix1 = (ix + dirs[j][0] + size) % size;
iy1 = iy + dirs[j][1];
Index1 = iy1 * size + ix1;
if (clusterMap[Index1] == 0.0) {
noNew = false;
if (psf[Index1] > maxValue) {
maxValue = psf[Index1];
IndexMax = Index1;
}
}
}
if (noNew)
pixelList.remove(listIndex); // remove current list element
else
listIndex++; // increase list index
}
if (maxValue == 0.0)
break; // no positive points left
if ((clusterSize >= minArea) && (psf[IndexMax] < theresholdLevel))
break; // level is below thershold, minimal size condition met
/* Add this new point to the list */
pixelList.add(IndexMax);
clusterSize++;
clusterMap[IndexMax] = 1.0;
cluster_energy += psf[IndexMax];
} // end of while ((pixelList.size()>0) && ...)
if (debugLevel > 3)
System.out.println("findClusterOnPSF: cluster size is " + clusterSize);
if (debugLevel > 3) {
ShowDoubleFloatArrays.showArrays(psf, size, size, title + "-psf");
}
if (debugLevel > 2) {
ShowDoubleFloatArrays.showArrays(clusterMap, size, size, title + "-clusterMap");
}
if (blurSigma > 0.0) {
DoubleGaussianBlur gb = new DoubleGaussianBlur();
gb.blurDouble(clusterMap, size, size, blurSigma, blurSigma, 0.01);
if (debugLevel > 2) {
ShowDoubleFloatArrays.showArrays(clusterMap, size, size, title + "-clusterMap-blured");
}
}
return clusterMap;
}
/* ======================================================================== */
/**
* Mask (assignes zero) to the flat-field array outside of the sample squares,]
* Clears grid nodes that do not have neighbors inside the sample squares. If
* (this.flatFieldForGrid==null) - creates mask of 1.0/0.0
*
* @param focusMeasurementParameters - parameters specifying probe points
*/
public void maskFocus(double x0, // lens center on the sensor
double y0, // lens center on the sensor
LensAdjustment.FocusMeasurementParameters focusMeasurementParameters) {
if (this.PATTERN_GRID == null) {
String msg = "PATTERN_GRID array does not exist, exiting";
IJ.showMessage("Error", msg);
throw new IllegalArgumentException(msg);
}
if (this.flatFieldForGrid == null) {
String msg = "Flat field for grid array does not exist, exiting";
IJ.showMessage("Error", msg);
throw new IllegalArgumentException(msg);
}
int[][] dirs = { { 1, 0 }, { 0, 1 }, { -1, 0 }, { 0, -1 }, { 1, 1 }, { 1, -1 }, { -1, 1 }, { -1, -1 } };
int width = getImageWidth();
int height = getImageHeight();
int halfSize = focusMeasurementParameters.sampleSize / 2;
// System.out.println("maskFocus(): width="+width+" height="+height+"
// _halfSize="+halfSize);
double[][][] sampleCoord = focusMeasurementParameters.sampleCoordinates(x0, // lens center on the sensor
y0); // lens center on the sensor
this.focusMask = new boolean[this.flatFieldForGrid.length];
for (int i = 0; i < this.focusMask.length; i++)
this.focusMask[i] = false;
for (int i = 0; i < focusMeasurementParameters.numSamples[1]; i++) {
// System.out.println(i+": y0="+y0);
for (int j = 0; j < focusMeasurementParameters.numSamples[0]; j++) {
int xs = (int) sampleCoord[i][j][0];
int ys = (int) sampleCoord[i][j][1];
// System.out.println(j+": xs="+xs);
for (int y = ys - halfSize; y < (ys + halfSize); y++)
if ((y >= 0) && (y < height)) {
for (int x = xs - halfSize; x < (xs + halfSize); x++)
if ((x >= 0) && (x < width)) {
this.focusMask[y * width + x] = true;
}
}
}
}
// Do we really need to zero the this.flatFieldForGrid where this.focusMask is
// false? We may need
// the pixels out of WOI to update grid around the needed nodes
// for (int i=0;i= 0) && (x < width) && (y >= 0) && (y < height) && this.focusMask[y * width + x])
maskUV[iUV[1]][iUV[0]] = true;
}
}
for (iUV[1] = 0; iUV[1] < maskUV.length; iUV[1]++)
for (iUV[0] = 0; iUV[0] < maskUV[0].length; iUV[0]++)
if (!maskUV[iUV[1]][iUV[0]]) {
boolean neibExists = false;
for (int d = 0; d < dirs.length; d++) {
int[] iUV1 = { iUV[0] + dirs[d][0], iUV[1] + dirs[d][1] };
if (isCellDefined(this.PATTERN_GRID, iUV1) && maskUV[iUV1[1]][iUV1[0]]) {
neibExists = true;
break;
}
}
if (!neibExists)
clearPatternGridCell(this.PATTERN_GRID, iUV);
}
}
/* ======================================================================== */
@Deprecated
public double[][] getPointersXY(ImagePlus imp, int numPointers) {
// try absolute grid calibratrion
// read image info to properties (if it was not done yet - should it?
if ((imp.getProperty("timestamp") == null) || (((String) imp.getProperty("timestamp")).length() == 0)) {
JP46_Reader_camera jp4_instance = new JP46_Reader_camera(false);
JP46_Reader_camera.decodeProperiesFromInfo(imp);
}
double[][] pointersXY = new double[numPointers][];
int numPointerDetected = 0;
for (int i = 0; i < pointersXY.length; i++) {
pointersXY[i] = null;
if ((imp.getProperty("POINTER_X_" + i) != null) && (imp.getProperty("POINTER_Y_" + i) != null)) {
pointersXY[i] = new double[2];
pointersXY[i][0] = Double.parseDouble((String) imp.getProperty("POINTER_X_" + i));
pointersXY[i][1] = Double.parseDouble((String) imp.getProperty("POINTER_Y_" + i));
numPointerDetected++;
}
}
if (numPointerDetected > 0)
return pointersXY;
else
return null;
}
// laserPointer used as a backup if images do not contain data, and specify if
// they are needed at all (null - skip)
public static double[][] getPointersXYUV(ImagePlus imp, LaserPointer laserPointer) {
int numPointers = (laserPointer != null) ? laserPointer.laserUVMap.length : 0;
// try absolute grid calibratrion
// read image info to properties (if it was not done yet - should it?
if ((imp.getProperty("timestamp") == null) || (((String) imp.getProperty("timestamp")).length() == 0)) {
JP46_Reader_camera jp4_instance = new JP46_Reader_camera(false);
JP46_Reader_camera.decodeProperiesFromInfo(imp);
}
double[][] pointersXY = new double[numPointers][];
int numPointerDetected = 0;
// Normally all pointers should either have or not U,V - the smallest one will
// later be used
for (int i = 0; i < pointersXY.length; i++) {
pointersXY[i] = null;
if ((imp.getProperty("POINTER_X_" + i) != null) && (imp.getProperty("POINTER_Y_" + i) != null)) {
if ((imp.getProperty("POINTER_U_" + i) != null) && (imp.getProperty("POINTER_V_" + i) != null)) {
pointersXY[i] = new double[4];
pointersXY[i][2] = Double.parseDouble((String) imp.getProperty("POINTER_U_" + i));
pointersXY[i][3] = Double.parseDouble((String) imp.getProperty("POINTER_V_" + i));
} else if ((laserPointer != null) &&(laserPointer.laserUVMap[i] != null)) {
pointersXY[i] = new double[4];
pointersXY[i][2] = laserPointer.laserUVMap[i][0];
pointersXY[i][3] = laserPointer.laserUVMap[i][1];
} else {
pointersXY[i] = new double[2];
}
pointersXY[i][0] = Double.parseDouble((String) imp.getProperty("POINTER_X_" + i));
pointersXY[i][1] = Double.parseDouble((String) imp.getProperty("POINTER_Y_" + i));
numPointerDetected++;
}
}
if (numPointerDetected > 0) {
return pointersXY;
} else {
return null;
}
}
public static void setPointersXYUV(ImagePlus imp, double[][] pointersXYUV) {
// JP46_Reader_camera jp4_instance= new JP46_Reader_camera(false);
// JP46_Reader_camera.decodeProperiesFromInfo(imp);
String prefix = "POINTER_";
Properties prop = imp.getProperties();
// Delete all properties starting with "POINTER_"
Enumeration