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 <http://www.gnu.org/licenses/>. ** -----------------------------------------------------------------------------** ** */ 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<patternCorr.length;i++)patternCorr[i]/= hsize; for (i = 0; i < 6; i++) patternCorr[i] /= hsize; /* Not linear Dx,Ex, Dy,Ey! */ if (this.debugLevel > 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<Integer> pixelList = new ArrayList<Integer>(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<barray.length;i++) { // barray[i]=simulationPattern.barray[i/simulationPattern.barray[0].length][i % // simulationPattern.barray[0].length]; // } // ShowDoubleFloatArrays.showArrays(barray, simulationPattern.barray[0].length, // simulationPattern.barray.length,"barray"); ShowDoubleFloatArrays.showArrays(simulationPattern.barray, "barray"); double[][][] scanXY = scanPatternCrossLocation(distortionParameters.correlationDx, // range (int) Math.round(distortionParameters.correlationDx / distortionParameters.correlationDy) + 1, centerXY0, // initial coordinates of the pattern cross point pattern[0][0], pattern[0][1], pattern[1][0], pattern[1][1], imp, // image data (Bayer mosaic) distortionParameters, // patternDetectParameters, matchSimulatedPatternCorr, // correlationSize thisSimulParameters, equalizeGreens, windowFunctionCorr, // window function simulationPattern, false, // if true - invert pattern null); // will create new instance of DoubleFHT class double[][] scanImg = new double[4][scanXY.length * scanXY[0].length]; // System.out.println("scanImg[0].length="+scanImg[0].length); for (i = 0; i < scanImg[0].length; i++) { scanImg[0][i] = scanXY[i / scanXY[0].length][i % scanXY[0].length][0]; scanImg[1][i] = scanXY[i / scanXY[0].length][i % scanXY[0].length][1]; scanImg[2][i] = scanXY[i / scanXY[0].length][i % scanXY[0].length][2]; scanImg[3][i] = scanXY[i / scanXY[0].length][i % scanXY[0].length][3]; } ShowDoubleFloatArrays.showArrays(scanImg, true, "scan_correlation"); ShowDoubleFloatArrays.showArrays(scanImg, false, "scan_correlation"); return; } /* * Try point x,y, test for pattern, return x,y, contrast (or null) [0][0] - x * [0][1] - y [0][2] - contrast [1][0] - Wave vector 1 x component [1][1] - Wave * vector 1 y component [1][2] - Wave vector 1 phase (not used here) [2][0] - * Wave vector 2 x component [2][1] - Wave vector 2 y component [2][2] - Wave * vector 2 phase (not used here) */ public double[][] tryPattern( LwirReaderParameters lwirReaderParameters, // null is OK DoubleFHT doubleFHT, double[] point, // xy to try 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 double[] bPattern, double[] windowFunction, double[] windowFunctionCorr, double[] windowFunctionCorr2, double[] windowFunctionCorr4, double[][] locsNeib, // which neibors to try (here - just the center) String dbgStr) { // this.debugLevel = 3; if (this.debugLevel == 3) { System.out.println("tryPattern(): this.debugLevel = 3"); } int debug_threshold = 2; if (imp == null) { if (dbgStr != null) System.out.println(dbgStr + " imp==null"); return null; } boolean is_lwir = ((lwirReaderParameters != null) && LwirReaderParameters.is_LWIR(imp)); int fft_size = is_lwir ? distortionParameters.FFTSize_lwir : distortionParameters.FFTSize; int xc = (int) (2 * Math.round(0.5 * point[0])); int yc = (int) (2 * Math.round(0.5 * point[1])); 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(); } Rectangle initialPatternCell = new Rectangle(xc - fft_size, yc - fft_size, 2 * fft_size, 2 * fft_size); if (is_lwir) {// move to to all? It requires twice FFT size to be able to select, maybe single // is still OK? initialPatternCell = new Rectangle(xc - fft_size / 2, yc - fft_size / 2, fft_size, fft_size); } if (!selection.contains(initialPatternCell)) { if (dbgStr != null) System.out.println(dbgStr + " selection (" + selection.x + "," + selection.y + "," + selection.width + "," + selection.height + ") does not contain cell (" + initialPatternCell.x + "," + initialPatternCell.y + "," + initialPatternCell.width + "," + initialPatternCell.height + ")"); return null; // area for FFT is not inside the initial selection } double[][] pattern = null; if (is_lwir) { // monochrome double[] dpixels = getNoBayer(imp, initialPatternCell); double[] dbg_dpixels = dpixels.clone(); normalizeAndWindow(dpixels, windowFunction); if (debugLevel > (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<Integer> waveFrontList = new ArrayList<Integer>(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<Integer> waveFrontList = new ArrayList<Integer>(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<GridNode> nodeQueue = new ConcurrentLinkedQueue<GridNode>(); 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<Integer> initialWave = new ArrayList<Integer>(); 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<triedIndices.length;i++) // System.out.print(triedIndices[i]?"+":"-"); // System.out.println(); 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(); // for (int n=seqNumber.getAndIncrement(); n< // numTries;n=seqNumber.getAndIncrement()){ for (int n = seqNumber.getAndIncrement(); n < (triedIndices.length - 1); n = seqNumber .getAndIncrement()) if (!triedIndices[n]) { if (nodeSet.get()) break; // already set 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 > 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<GridNode> 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<GridNode> nodeQueue = new ConcurrentLinkedQueue<GridNode>(); 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<Integer> extList = new ArrayList<Integer>(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<extList.size();i++){ for (int i = pixInWaveNum.getAndIncrement(); i < extList.size(); i = pixInWaveNum .getAndIncrement()) { Integer Indx = extList.get(i); int iy = Indx / sWidth; int ix = Indx % sWidth; double S0 = 0.0; double SF = 0.0; double SX = 0.0; double SY = 0.0; double SFX = 0.0; double SFY = 0.0; double SX2 = 0.0; double SY2 = 0.0; double SXY = 0.0; int iYmin = iy - len; if (iYmin < 0) iYmin = 0; int iYmax = iy + len; if (iYmax >= 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<Integer> 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<Integer> 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]<grid.length) && // (uv[0]<grid[uv[1]].length) && (grid[uv[1]][uv[0]]==null); // 4-th element is added to mark that the cell is dleted, but keep coordinates 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]].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<Integer> pixelList = new ArrayList<Integer>(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] // <theresholdLevel) /* Find maximal new neighbor */ maxValue = 0.0; listIndex = 0; while (listIndex < pixelList.size()) { Index = pixelList.get(listIndex); iy = Index / size; ix = Index % size; noNew = true; for (int j = 0; j < 8; j++) if (((iy > 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<this.focusMask.length;i++) if (!this.focusMask[i]) // this.flatFieldForGrid[i]=0.0; if (this.PATTERN_GRID.length == 0) return; boolean[][] maskUV = new boolean[this.PATTERN_GRID.length][this.PATTERN_GRID[0].length]; int[] iUV = { 0, 0 }; for (iUV[1] = 0; iUV[1] < maskUV.length; iUV[1]++) for (iUV[0] = 0; iUV[0] < maskUV[0].length; iUV[0]++) { maskUV[iUV[1]][iUV[0]] = false; if (isCellDefined(this.PATTERN_GRID, iUV)) { int x = (int) Math.round(this.PATTERN_GRID[iUV[1]][iUV[0]][0][0]); int y = (int) Math.round(this.PATTERN_GRID[iUV[1]][iUV[0]][0][1]); if ((x >= 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<Object> enumKey = prop.keys(); while (enumKey.hasMoreElements()) { Object key = enumKey.nextElement(); if (((String) key).startsWith(prefix)) { prop.remove(key); } } // Delete all properties starting with "POINTER_" int num_used = 0; for (int i = 0; i < pointersXYUV.length; i++) if (pointersXYUV[i] != null) { prop.setProperty(prefix + "X_" + i, "" + pointersXYUV[i][0]); prop.setProperty(prefix + "Y_" + i, "" + pointersXYUV[i][1]); if (pointersXYUV[i].length > 2) { prop.setProperty(prefix + "U_" + i, "" + pointersXYUV[i][2]); prop.setProperty(prefix + "V_" + i, "" + pointersXYUV[i][3]); } num_used++; } prop.setProperty("USED_POINTERS", "" + num_used); // jp4_instance.encodeProperiesToInfo(imp); // (new FileSaver(imp)).saveAsTiff(path); } /* ======================================================================== */ public String getChannel(ImagePlus imp) { // 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); } if (imp.getProperty("channel") == null) return null; return (String) imp.getProperty("channel"); } /* ======================================================================== */ public void showFlatFieldForGrid() { if (this.flatFieldForGrid != null) ShowDoubleFloatArrays.showArrays(this.flatFieldForGrid, getImageWidth(), getImageHeight(), "Flat_field_for_grid"); } public void showFFCorrectedGrid() { if (this.gridFFCorr != null) ShowDoubleFloatArrays.showArrays(this.gridFFCorr, getImageWidth(), getImageHeight(), "Flat_field_corrected_grid"); } public void showFocusMask() { if (this.focusMask != null) { double[] dfm = new double[this.focusMask.length]; for (int i = 0; i < dfm.length; i++) dfm[i] = this.focusMask[i] ? 1.0 : 0.0; ShowDoubleFloatArrays.showArrays(dfm, getImageWidth(), getImageHeight(), "Focus_mask"); } } public void showUVIndex() { if (this.UV_INDEX != null) { double[] uv = new double[this.UV_INDEX.length]; for (int i = 0; i < uv.length; i++) uv[i] = this.UV_INDEX[i]; ShowDoubleFloatArrays.showArrays(uv, getImageWidth(), getImageHeight(), "UV_INDEX"); } } public boolean patternOK() { return (this.PATTERN_GRID != null); } public double[][][][] getDArray() { return this.PATTERN_GRID; } public double[][][] getDArray(int v) { return this.PATTERN_GRID[v]; } public double[][] getDArray(int v, int u) { return this.PATTERN_GRID[v][u]; } public double[] getDArray(int v, int u, int n) { return this.PATTERN_GRID[v][u][n]; } public double getDArray(int v, int u, int n, int k) { return this.PATTERN_GRID[v][u][n][k]; } public double[] getXY(int v, int u) { return this.PATTERN_GRID[v][u][0]; } public int getDArrayHeight() { if (this.PATTERN_GRID == null) return 0; return this.PATTERN_GRID.length; } public int getDArrayWidth() { if (this.PATTERN_GRID == null) return 0; return this.PATTERN_GRID[0].length; } // matchSimulatedPattern.DIST_SELECTION.width, // image (mask) width public Rectangle getWOI() { return this.DIST_SELECTION; } public int getImageWidth() { return this.UV_INDEX_WIDTH; } public int getImageHeight() { if (this.UV_INDEX == null) return 0; return this.UV_INDEX.length / this.UV_INDEX_WIDTH; } public void setWOI(Rectangle woi) { this.DIST_SELECTION = new Rectangle(woi); } public void setWOI(int x, int y, int w, int h) { this.DIST_SELECTION = new Rectangle(x, y, w, h); } public int[] getUVIndex() { return this.UV_INDEX; } public int getUVIndex(int i) { if ((i < 0) || (i >= this.UV_INDEX.length)) return -1; // let it throw exception return this.UV_INDEX[i]; } public int getUVIndex(int x, int y) { if ((x < 0) || (y < 0) || (x >= this.PATTERN_GRID[0].length) || (y >= this.PATTERN_GRID.length)) return -1; return this.UV_INDEX[x + UV_INDEX_WIDTH * y]; } /** * Returns a pair of U,V from xy, integer result (does not interpolate * * @param x - pixel coordinat X * @param y - pixel coordinat Y * @return {u,v} pair */ public int[] getUV(int x, int y) { if ((x < 0) || (y < 0) || (x >= this.UV_INDEX_WIDTH) || (y >= (this.UV_INDEX.length / this.UV_INDEX_WIDTH))) return null; // out of UV_INDEX bounds if ((x + this.UV_INDEX_WIDTH * y) > this.UV_INDEX.length) { if (this.debugLevel > 0) { System.out.println("getUV(" + x + "," + y + "): this.UV_INDEX.length=" + this.UV_INDEX.length + ", this.UV_INDEX_WIDTH=" + this.UV_INDEX_WIDTH); } return null; } int index = this.UV_INDEX[x + this.UV_INDEX_WIDTH * y]; if (index < 0) return null; // <0 - undefined int width = this.PATTERN_GRID[0].length; int[] uv = { index % width, index / width }; return uv; } /** * Map estimated grid hintGrid to measured * * @param hintGrid [v][u][ 0-x, 1-y, 2 - u, 3- v] * @param searchAround how far (in pixels) to look for the nearest defined one * if the specified is undefined * @return array [v][u] [0 - measured u, 1 - measured v, 2 - measured contrast] * ([v][u]==null no measured grid there */ public double[][][] mapEstimatedToMeasured(double[][][] hintGrid, double searchAround) { double[][][] measuredUV = new double[hintGrid.length][hintGrid[0].length][]; for (int v = 0; v < measuredUV.length; v++) for (int u = 0; u < measuredUV[0].length; u++) if (hintGrid[v][u] != null) measuredUV[v][u] = getUVLinear(hintGrid[v][u][0], hintGrid[v][u][1], searchAround); else measuredUV[v][u] = null; return measuredUV; } /** * Calculate linear matrix (2x3) parameters measured grid UV from estimated grid * UV * * @param hintGrid [v][u][ 0-x, 1-y, 2 - u, 3- v] * @param searchAround how far (in pixels) to look for the nearest defined one * if the specified is undefined * @return {{Au, Bu, Cu}.{Av, Bc, Cv}}; where Umeas=Au*Uhint+Bu*Vhint+Cu, * Vmeas=Av*Uhint+Bv*Vhint+Cv */ public double[][] calcGridMatchMatrix(double[][][] hintGrid, double searchAround) { double[][][] measuredUV = mapEstimatedToMeasured(hintGrid, searchAround); // contrast - minimal of the ones // around if (this.debugLevel > 2) { double[][] pixels = new double[9][measuredUV.length * measuredUV[0].length]; int index = 0; String[] titles = { "grid-U", "grid-V", "contrast", "hint-X", "hint-Y", "hint-U", "hint-V", "grid-hint-U", "grid-hint-V" }; // last 2 only valid for rotation==0 for (int v = 0; v < measuredUV.length; v++) for (int u = 0; u < measuredUV[v].length; u++) { if (measuredUV[v][u] != null) { for (int i = 0; i < 3; i++) pixels[i][index] = measuredUV[v][u][i]; for (int i = 0; i < 4; i++) pixels[i + 3][index] = hintGrid[v][u][i]; for (int i = 0; i < 2; i++) pixels[i + 7][index] = measuredUV[v][u][i] - hintGrid[v][u][i + 2]; } else { for (int i = 0; i < 9; i++) pixels[i][index] = Double.NaN; } index++; } ShowDoubleFloatArrays.showArrays(pixels, measuredUV[0].length, measuredUV.length, true, "measuredUV", titles); } int numDefined = 0; for (int v = 0; v < measuredUV.length; v++) for (int u = 0; u < measuredUV[v].length; u++) if (measuredUV[v][u] != null) numDefined++; double[][][] data = new double[numDefined][3][];// double [][][] data =new double [numDefined][2][2]; int index = 0; for (int v = 0; v < measuredUV.length; v++) for (int u = 0; u < measuredUV[v].length; u++) if (measuredUV[v][u] != null) { data[index][0] = new double[2]; data[index][1] = new double[2]; data[index][2] = new double[1]; data[index][0][0] = hintGrid[v][u][2]; // hinted U data[index][0][1] = hintGrid[v][u][3]; // hinted V data[index][1][0] = measuredUV[v][u][0]; // measured U data[index][1][1] = measuredUV[v][u][1]; // measured V data[index][2][0] = measuredUV[v][u][2]; // contrast index++; } if (this.debugLevel > 0) { System.out.println( "calcGridMatchMatrix(), data.length=" + data.length + " measuredUV.length=" + measuredUV.length + ((measuredUV.length > 0) ? (", measuredUV[0].length=" + measuredUV[0].length) : "")); if (this.debugLevel > 3) for (index = 0; index < data.length; index++) { System.out.println(data[index][0][0] + "," + data[index][0][1] + "," + data[index][1][0] + "," + data[index][1][1] + "," + data[index][2][0]); } } // int gridRotation=-1; //undefined if (data.length < 3) { /// if (this.debugLevel>0) System.out.println("calcGridMatchMatrix(), /// data.length="+data.length+" measuredUV.length="+measuredUV.length+ /// ((measuredUV.length>0)?(", /// measuredUV[0].length="+measuredUV[0].length):"")); return null; } double[][] coeff = new PolynomialApproximation(this.debugLevel).quadraticApproximation(data, true); if (coeff != null) { // gridRotation=matrixToRot(coeff); int rot = matrixToRot(coeff); boolean[] flips = rotToFlips(rot); double[][] aI = { { 1, 0 }, { 0, 1 } }; double[][] aSwap = { { 0, 1 }, { 1, 0 } }; double[][] aFlipU = { { -1, 0 }, { 0, 1 } }; double[][] aFlipV = { { 1, 0 }, { 0, -1 } }; Matrix M = new Matrix(aI); if (flips[0]) M = M.times((new Matrix(aSwap))); if (flips[1]) M = M.times((new Matrix(aFlipU))); if (flips[2]) M = M.times((new Matrix(aFlipV))); // now M reconstructs coeff double[][] aM = M.getArray(); double SMU = 0.0, SMV = 0.0, SW = 0.0; for (int i = 0; i < data.length; i++) { SMU += data[i][2][0] * (data[i][1][0] - (aM[0][0] * data[i][0][0] + aM[0][1] * data[i][0][1])); SMV += data[i][2][0] * (data[i][1][1] - (aM[1][0] * data[i][0][0] + aM[1][1] * data[i][0][1])); SW += data[i][2][0]; } SMU /= SW; SMV /= SW; if (this.debugLevel > 0) { System.out.println( "coeff[0][0]=" + coeff[0][0] + " coeff[0][1]=" + coeff[0][1] + " coeff[0][2]=" + coeff[0][2]); System.out.println( "coeff[1][0]=" + coeff[1][0] + " coeff[1][1]=" + coeff[1][1] + " coeff[1][2]=" + coeff[1][2]); System.out.println("aM[0][0]=" + aM[0][0] + " aM[0][1]=" + aM[0][1] + " SMU=" + SMU); System.out.println("aM[1][0]=" + aM[1][0] + " aM[1][1]=" + aM[1][1] + " SMV=" + SMV); } coeff[0][0] = aM[0][0]; coeff[0][1] = aM[0][1]; coeff[1][0] = aM[1][0]; coeff[1][1] = aM[1][1]; coeff[0][2] = SMU; coeff[1][2] = SMV; } else { if (this.debugLevel > 0) { System.out.println("coeff == null"); } } return coeff; } public int matrixToRot(double[][] coeff) { boolean[] flips = { false, false, false }; double[][] aR = { { coeff[0][0], coeff[0][1] }, { coeff[1][0], coeff[1][1] } }; double[][] aSwap = { { 0, 1 }, { 1, 0 } }; Matrix R = new Matrix(aR); if ((aR[0][0] * aR[0][0] + aR[1][1] * aR[1][1]) < (aR[1][0] * aR[1][0] + aR[0][1] * aR[0][1])) { flips[0] = true; R = (new Matrix(aSwap)).times(R); } flips[1] = R.getArray()[0][0] < 0; flips[2] = R.getArray()[1][1] < 0; return flipsToRot(flips[0], flips[1], flips[2]); } public int[][] gridMatrixApproximate(double[][] coeff, boolean invert) { int rot = matrixToRot(coeff); boolean[] flips = rotToFlips(rot); double[][] aI = { { 1, 0 }, { 0, 1 } }; double[][] aSwap = { { 0, 1 }, { 1, 0 } }; double[][] aFlipU = { { -1, 0 }, { 0, 1 } }; double[][] aFlipV = { { 1, 0 }, { 0, -1 } }; Matrix M = new Matrix(aI); if (flips[0]) M = M.times((new Matrix(aSwap))); if (flips[1]) M = M.times((new Matrix(aFlipU))); if (flips[2]) M = M.times((new Matrix(aFlipV))); // now M reconstructs coeff double[][] aM = M.getArray(); // Black/white cells have to be flipped if flipU XOR flipW, regardless of swapUV int flipForWhite = (invert ^ flips[1] ^ flips[2]) ? 1 : 0; int[][] shifts = { { 2 * ((int) Math.round(0.5 * (coeff[0][2] + 0))), 2 * ((int) Math.round(0.5 * (coeff[1][2] + flipForWhite))) - flipForWhite }, { 2 * ((int) Math.round(0.5 * (coeff[0][2] - 1))) + 1, 2 * ((int) Math.round(0.5 * (coeff[1][2] + flipForWhite - 1))) + 1 - flipForWhite } }; int shiftSelect = (((shifts[0][0] - coeff[0][2]) * (shifts[0][0] - coeff[0][2]) + (shifts[0][1] - coeff[1][2]) * (shifts[0][1] - coeff[1][2])) > ((shifts[1][0] - coeff[0][2]) * (shifts[1][0] - coeff[0][2]) + (shifts[1][1] - coeff[1][2]) * (shifts[1][1] - coeff[1][2]))) ? 1 : 0; if (this.debugLevel > 0) { double d1 = Math.sqrt((shifts[0][0] - coeff[0][2]) * (shifts[0][0] - coeff[0][2]) + (shifts[0][1] - coeff[1][2]) * (shifts[0][1] - coeff[1][2])); double d2 = Math.sqrt((shifts[1][0] - coeff[0][2]) * (shifts[1][0] - coeff[0][2]) + (shifts[1][1] - coeff[1][2]) * (shifts[1][1] - coeff[1][2])); System.out.println("gridMatrixApproximate(): shifts[0][0]=" + shifts[0][0] + " shifts[0][1]=" + shifts[0][1] + " shifts[1][0]=" + shifts[1][0] + " shifts[1][1]=" + shifts[1][1] + " shiftSelect=" + shiftSelect + " d1=" + d1 + " d2=" + d2); } int[][] iCoeff = { { (int) Math.round(aM[0][0]), (int) Math.round(aM[0][1]), shifts[shiftSelect][0] }, { (int) Math.round(aM[1][0]), (int) Math.round(aM[1][1]), shifts[shiftSelect][1] } }; return iCoeff; } // old version, no distinction between B and W public int[][] gridMatrixApproximateNoBW(double[][] coeff) { int rot = matrixToRot(coeff); boolean[] flips = rotToFlips(rot); double[][] aI = { { 1, 0 }, { 0, 1 } }; double[][] aSwap = { { 0, 1 }, { 1, 0 } }; double[][] aFlipU = { { -1, 0 }, { 0, 1 } }; double[][] aFlipV = { { 1, 0 }, { 0, -1 } }; Matrix M = new Matrix(aI); if (flips[0]) M = M.times((new Matrix(aSwap))); if (flips[1]) M = M.times((new Matrix(aFlipU))); if (flips[2]) M = M.times((new Matrix(aFlipV))); // now M reconstructs coeff double[][] aM = M.getArray(); int[][] iCoeff = { { (int) Math.round(aM[0][0]), (int) Math.round(aM[0][1]), (int) Math.round(coeff[0][2]) }, { (int) Math.round(aM[1][0]), (int) Math.round(aM[1][1]), (int) Math.round(coeff[1][2]) } }; return iCoeff; } public double worstGridMatchRotSkew( double[][] coeff) { return worstGridMatchRotSkew(coeff, false); } public double worstGridMatchRotSkew( double[][] coeff, boolean invert ) { int[][] iCoeff = gridMatrixApproximate(coeff, invert); double worst = 0; for (int i = 0; i < 2; i++) for (int j = 0; j < 2; j++) { double d = Math.abs(coeff[i][j] - iCoeff[i][j]); if (d > worst) worst = d; } return worst; } public double worstGridMatchTranslate( double[][] coeff) { // in grids half-periods, not pixels! return worstGridMatchTranslate(coeff, false); } public double worstGridMatchTranslate( double[][] coeff, boolean invert) { // in grids half-periods, not pixels! int[][] iCoeff = gridMatrixApproximate(coeff, invert); double worst = 0; for (int i = 0; i < 2; i++) { double d = Math.abs(coeff[i][2] - iCoeff[i][2]); if (d > worst) worst = d; } return worst; } // searchAround /** * Find double u,v from double x,y by linear interpolation from neighbor cells. * Requires this.UV_INDEX to be calculated and matching this.PATTERN_GRID * * @param x pixel coordinate * @param y pixel coordinate * @param searchAround how far (in pixels) to look for the nearest defined one * if the specified is undefined * @return uv pair or null - modified - triplet, last - contrast */ public double[] getUVLinear(double x, double y, double searchAround) { int ix0 = (int) Math.round(x); int iy0 = (int) Math.round(y); int ix = ix0, iy = iy0; int[] uv0 = getUV(ix, iy); double best2 = searchAround * searchAround + 1; // if the point is slightly out of grid - find the one near if (uv0 == null) { for (int iy1 = iy - ((int) Math.round(searchAround)); iy1 >= iy + searchAround; iy1++) for (int ix1 = ix - ((int) Math.round(searchAround)); ix1 >= ix + searchAround; ix1++) { double d = (ix1 - ix) * (ix1 - ix) + (iy1 - iy) * (iy1 - iy); if (d < best2) { uv0 = getUV(ix1, iy1); if (uv0 != null) { best2 = d; ix = ix1; iy = iy1; } } } } if (uv0 == null) return null; // no grid points near int[][] dirDiffs = { { 1, 1 }, { -1, 1 }, { 1, -1 }, { -1, -1 } }; double[] xy0 = cellXYC(uv0); double[] xy1 = null; double[] xy2 = null; int[] deltaUV = null; for (int dir = 0; dir < dirDiffs.length; dir++) { xy1 = cellXYC(uv0[0] + dirDiffs[dir][0], uv0[1]); xy2 = cellXYC(uv0[0], uv0[1] + dirDiffs[dir][1]); if ((xy1 != null) && (xy2 != null)) { deltaUV = new int[2]; deltaUV[0] = dirDiffs[dir][0]; deltaUV[1] = dirDiffs[dir][1]; break; } } double minContrast = Math.min(Math.min(xy1[2], xy2[2]), xy0[2]); // minimal contrast off all 3 points if (deltaUV == null) return null; // could not find 2 orthogonal neighbors to interpolate /* * x=xy0[0] + dU*deltaUV[0]*(xy1[0]-xy0[0])+dV*deltaUV[1]*(xy2[0]-xy0[0]) * y=xy0[1] + dU*deltaUV[0]*(xy1[1]-xy0[1])+dV*deltaUV[1]*(xy2[1]-xy0[1]) * */ double[][] aM = { { deltaUV[0] * (xy1[0] - xy0[0]), deltaUV[1] * (xy2[0] - xy0[0]) }, { deltaUV[0] * (xy1[1] - xy0[1]), deltaUV[1] * (xy2[1] - xy0[1]) } }; Matrix M = new Matrix(aM); double[][] aB = { { x - xy0[0] }, { y - xy0[1] } }; Matrix B = new Matrix(aB); if (!(new LUDecomposition(M)).isNonsingular()) { System.out.println("getUVLinear(" + x + "," + y + "): Matix is singular:"); M.print(10, 6); return null; } double[] dUV = M.solve(B).getRowPackedCopy(); double[] result = { uv0[0] + dUV[0], uv0[1] + dUV[1], minContrast }; return result; } public void invalidateAll() { invalidateCalibration(); invalidateFlatFieldForGrid(); invalidateFocusMask(); } private void invalidateCalibration() { this.reMap = null; // invalidate if any this.targetUV = null; // invalidate if any this.pXYUV = null; // invalidate if any this.passNumber = 1; resetCorrelationSizesUsed(); // reset which FFT sizes where used in correlation } public void invalidateFlatFieldForGrid() { this.flatFieldForGrid = null; // reset flat field for grid this.gridContrastBrightness = null; } public void invalidateFocusMask() { this.focusMask = null; } /* * get height and width of the measured pattern array applies to PATTERN_GRID, * targetUV and pixelsUV */ public int getHeight() { if (this.pXYUV == null) return 0; return this.pXYUV.length; } public int getWidth() { if (this.pXYUV == null) return 0; return this.pXYUV[0].length; } /* * Get physical target UV pair from measured pattern. Requires absolute mapping * (by laser spots) Pair may be null if no pattern is detected for this node in * the image */ public int[][][] getTargetUV() { return this.targetUV; } /* * Get pixel X,Y pair for each node in the measured pattern. Calculated during * absolute mapping (by laser spots) Pair may be null if no pattern is detected * for this node in the image */ public double[][][] getPXYUV() { return this.pXYUV; } public int restorePatternGridFromGridList(double[][][] pixelsXYSet, int[][][] pixelsUVSet, double[] intensityRange) { double maxX = 0, maxY = 0; for (int n = 0; n < pixelsXYSet.length; n++) { for (int i = 0; i < pixelsXYSet[n].length; i++) { if (pixelsXYSet[n][i][0] > maxX) maxX = pixelsXYSet[n][i][0]; if (pixelsXYSet[n][i][1] > maxY) maxY = pixelsXYSet[n][i][1]; } } int width = (int) Math.ceil(maxX) + 1; int height = (int) Math.ceil(maxY) + 1; return restorePatternGridFromGridList(pixelsXYSet, pixelsUVSet, width, height, intensityRange); } /** * Restore this.PATTERN_GRID array (no wave vectors) from the lists use in * Distortions class * * @param pixelsXYSet list of the {x,y} pairs for each grid node (now - a pair * of lists used pixels and those that did not fit into * physical target) * @param pixelsUVSet list of {u,v} pairs for each grid node (now - a pair of * lists) * @param width sensor (image) width * @param height sensor (image) height * @return number of cells in the grid */ public int restorePatternGridFromGridList(double[][][] pixelsXYSet, int[][][] pixelsUVSet, int width, int height, double[] intensityRange) { int numCells = 0; int minU = 0, minV = 0, maxU = 0, maxV = 0; this.PATTERN_GRID = null; setWOI(0, 0, 0, 0); if ((pixelsXYSet != null) && (pixelsUVSet != null) && (pixelsXYSet.length > 0)) { setWOI(0, 0, width, height);// set WOI for the current image for (int n = 0; n < pixelsXYSet.length; n++) for (int i = 0; i < pixelsXYSet[n].length; i++) if ((pixelsXYSet[n][i] != null) && (pixelsUVSet[n][i] != null)) { if (numCells == 0) { minV = pixelsUVSet[n][i][1]; maxV = pixelsUVSet[n][i][1]; minU = pixelsUVSet[n][i][0]; maxU = pixelsUVSet[n][i][0]; } else { if (minV > pixelsUVSet[n][i][1]) minV = pixelsUVSet[n][i][1]; else if (maxV < pixelsUVSet[n][i][1]) maxV = pixelsUVSet[n][i][1]; if (minU > pixelsUVSet[n][i][0]) minU = pixelsUVSet[n][i][0]; else if (maxU < pixelsUVSet[n][i][0]) maxU = pixelsUVSet[n][i][0]; } numCells++; } if (numCells > 0) { // do not break black/white correspondence, always move by even number of cells if ((minU & 1) != 0) minU--; if ((minV & 1) != 0) minV--; this.minUV[0] = minU; this.minUV[1] = minV; // save shift to restore later this.PATTERN_GRID = setPatternGridArray(maxU - minU + 1, maxV - minV + 1); this.gridContrastBrightness = new double[4][this.PATTERN_GRID.length][this.PATTERN_GRID[0].length]; // {grid // contrast, // grid // intensity // red, // grid // intensity // green, // grid // intensity // blue}[v][u] for (int n = 0; n < 4; n++) for (int v = 0; v < this.gridContrastBrightness[0].length; v++) for (int u = 0; u < this.gridContrastBrightness[0][0].length; u++) this.gridContrastBrightness[n][v][u] = 0.0; for (int n = 0; n < pixelsXYSet.length; n++) for (int i = 0; i < pixelsXYSet[n].length; i++) if ((pixelsXYSet[n][i] != null) && (pixelsUVSet[n][i] != null)) { int[] shiftedUV = { pixelsUVSet[n][i][0] - minU, pixelsUVSet[n][i][1] - minV }; setPatternGridCell(this.PATTERN_GRID, shiftedUV, // pixelsUV[i], pixelsXYSet[n][i], // will have extra data null, null); this.gridContrastBrightness[0][shiftedUV[1]][shiftedUV[0]] = pixelsXYSet[n][i][2]; this.gridContrastBrightness[1][shiftedUV[1]][shiftedUV[0]] = pixelsXYSet[n][i][3] * intensityRange[0]; this.gridContrastBrightness[2][shiftedUV[1]][shiftedUV[0]] = pixelsXYSet[n][i][4] * intensityRange[1]; this.gridContrastBrightness[3][shiftedUV[1]][shiftedUV[0]] = pixelsXYSet[n][i][5] * intensityRange[2]; } } } return numCells; } /** * Create PATTERN_GRID for calculated grid (for sensor parameters, orientation) * for debugging purposes * * @param hintGrid grid array [v][u][0- x, 1 - y, 2 - u, 3 - v] (u,v - not used * here) * @param width image width, in pixels * @param height image height, in pixels * @return number of non-empty cells */ public int restoreSimulatedPatternGridFromHint(double[][][] hintGrid, int width, int height) { int numCells = 0; this.PATTERN_GRID = null; if (hintGrid == null) return 0; setWOI(0, 0, width, height);// set WOI for the current image // this.PATTERN_GRID=setPatternGridArray(hintGrid[0].length+1,hintGrid.length+1); this.PATTERN_GRID = setPatternGridArray(hintGrid[0].length, hintGrid.length); for (int v = 0; v < hintGrid.length; v++) for (int u = 0; u < hintGrid[v].length; u++) if (hintGrid[v][u] != null) { double[] xy = { hintGrid[v][u][0], hintGrid[v][u][1] }; if ((xy[0] >= 0) && (xy[1] >= 0) && (xy[0] < width) && (xy[1] < height)) { int[] uv = { u, v }; // if ((xy[0]==0) && (xy[1]==0)) { // System.out.println("x==0,y==0 for u="+u+", v="+v+", // hintGrid[v][u][2]="+hintGrid[v][u][2]+", // hintGrid[v][u][3]="+hintGrid[v][u][3]); // } setPatternGridCell(this.PATTERN_GRID, uv, xy, null, null); numCells++; } } return numCells; } /** * restore grid parameters - this.PATTERN_GRID (no wave vectors) only absolute * calibration (if any) is lost, only orientation is preserved * * @param imp_grid - grid encoded as image * @return array of laser pointers coordinates - no separate, return number of * grid cells */ public int restorePatternGridFromImage(ImagePlus imp_grid) { int numCells = 0; this.PATTERN_GRID = null; setWOI(0, 0, 0, 0); if (imp_grid != null) { setWOI(0, 0, imp_grid.getWidth(), imp_grid.getHeight());// set WOI for the current image ImageStack stack = imp_grid.getStack(); float[][] pixels = new float[4][]; if ((stack == null) || (stack.getSize() != 4)) { String msg = "Expected a 4-slice stack in " + imp_grid.getTitle(); IJ.showMessage("Error", msg); throw new IllegalArgumentException(msg); } for (int i = 0; i < 4; i++) pixels[i] = (float[]) stack.getPixels(i + 1); // pixel X : negative - no grid here int minU = 0, minV = 0, maxU = 0, maxV = 0; for (int i = 0; i < pixels[0].length; i++) if (pixels[0][i] >= 0) { int u = Math.round(pixels[2][i]); int v = Math.round(pixels[3][i]); if (numCells == 0) { minV = v; maxV = v; minU = u; maxU = u; } else { if (minV > v) minV = v; else if (maxV < v) maxV = v; if (minU > u) minU = u; else if (maxU < u) maxU = u; } numCells++; } if (numCells > 0) { // setPatternGridArray(maxU - minU + 1, maxV - minV + 1); // FIXME: Does nothing this.PATTERN_GRID = setPatternGridArray(maxU - minU + 1, maxV - minV + 1); // FIXME: Does nothing double[] xy = new double[2]; int[] uv = new int[2]; for (int i = 0; i < pixels[0].length; i++) if (pixels[0][i] >= 0) { uv[0] = (Math.round(pixels[2][i])) - minU; uv[1] = (Math.round(pixels[3][i])) - minV; xy[0] = pixels[0][i]; xy[1] = pixels[1][i]; setPatternGridCell(this.PATTERN_GRID, uv, xy, null, null); } } } return numCells; } /** * Calculate this.UV_INDEX (and this.UV_INDEX_WIDTH) - map from image pixel to * U,V (U*this.UV_INDEX_WIDTH*V). this.DIST_SELECTION should be set * * @param imp source image (just to find image size, null - use * this.DIST_SELECTION.width, * this.DIST_SELECTION.height) * @param shiftXY pattern shift (from debug), null - use {0,0} * @param threadsMax limit on threads to use * @param updateStatus update ImageJ status bar * @param global_debug_level global debug level * @param debug_level loop debug level * @return true - OK, false - failure */ public boolean createUV_INDEX(ImagePlus imp, // or null - just to determine WOI (when getWOI matches image size) double[] shiftXY, // add to patterGrid xy, null OK int threadsMax, boolean updateStatus, int global_debug_level, // DEBUG_LEVEL int debug_level // debug level used inside loops ) { SimulationPattern simulationPattern = new SimulationPattern(); // do not need bitmap array here float[] UV_float0 = simulationPattern.simulateGrid(getDArray(), 2, // gridFrac, // number of grid steps per // pattern full period null, // simulParameters, getWOI(), 1, // SIMUL.subdiv/2, shiftXY, // add to patterGrid xy, null OK threadsMax, updateStatus, debug_level); // debug level if (UV_float0 == null) { System.out.println("BUG: createUV_INDEX(): simulationPattern.simulateGrid() returnerd null"); System.out.println( "BUG: createUV_INDEX(): getDArray() returnerd " + ((getDArray() == null) ? "null" : "noy null")); return false; } float[] UV_float = simulationPattern.combineWithCanvas(-1.0, ((imp == null) ? (getWOI().width) : imp.getWidth()), ((imp == null) ? (getWOI().height) : imp.getHeight()), getWOI(), UV_float0); if (global_debug_level > 3) ShowDoubleFloatArrays.showArrays(UV_float0, getWOI().width, getWOI().height, "UV_float0"); // all -1 if (global_debug_level > 2) ShowDoubleFloatArrays.showArrays(UV_float, ((imp == null) ? (getWOI().width) : imp.getWidth()), ((imp == null) ? (getWOI().height) : imp.getHeight()), "UV_float"); this.UV_INDEX = new int[UV_float.length]; this.UV_INDEX_WIDTH = ((imp == null) ? (getWOI().width) : imp.getWidth()); for (int i = 0; i < this.UV_INDEX.length; i++) this.UV_INDEX[i] = (int) UV_float[i]; return true; } /** * Apply hint grid and laser pointer calibration to the grid. If (hintGrid!= * null) && (hintGridTolerance>0) than any result >=0 means match Create * this.pixelsUV, this.targetUV * * @param laserPointer Laser pointer parameters DEPRECATE, move to * pointersXYUV * @param pointersXY pairs of detected pointers x,y (or nulls) * @param removeOutOfGridPointers if true - remove pointers if they are outside * of the pattern grid * @param hintGrid predicted grid array (or null) * @param hintGridTolerance allowed mismatch (fraction of period) or 0 - * orientation only * @param global_debug_level debug level * @param noMessageBoxes do not open (and wait for) dialog boxes * @return >=0 - number of laser pointers used for calibration, <0 - different * errors */ public int combineGridCalibration_deprecated(LaserPointer laserPointer, // LaserPointer object or null double[][] pointersXY, boolean removeOutOfGridPointers, // double[][][] hintGrid, // predicted grid array (or null) double hintGridTolerance, // alllowed mismatch (fraction of period) or 0 - orientation only int global_debug_level, // DEBUG_LEVEL boolean noMessageBoxes) { int acalibrated = 0; double[][] gridMatchCoeff = null; double searchAround = 20.0; // how far to look for the grid node int gridRotation = -1; // undefined int[] iGridTranslateUV = null; // translate UV grid by these integer numbers if (hintGrid != null) { gridMatchCoeff = calcGridMatchMatrix(hintGrid, searchAround); // now already rounds rotate and re-replaces gridMatchCoeff with approximated, // refines gridMatchCoeff[0][2] and gridMatchCoeff[1][2] if (gridMatchCoeff != null) { gridRotation = matrixToRot(gridMatchCoeff); this.debugLevel = global_debug_level; int[][] iGridMatchCoeff = gridMatrixApproximate(gridMatchCoeff, false); if (global_debug_level > 1) { System.out.println("gridMatchCoeff[0]={" + IJ.d2s(gridMatchCoeff[0][0], 5) + ", " + IJ.d2s(gridMatchCoeff[0][1], 5) + ", " + IJ.d2s(gridMatchCoeff[0][2], 5) + "}"); System.out.println("gridMatchCoeff[1]={" + IJ.d2s(gridMatchCoeff[1][0], 5) + ", " + IJ.d2s(gridMatchCoeff[1][1], 5) + ", " + IJ.d2s(gridMatchCoeff[1][2], 5) + "}"); System.out.println("gridRotation=" + gridRotation); System.out.println("iGridMatchCoeff[0]={" + iGridMatchCoeff[0][0] + ", " + iGridMatchCoeff[0][1] + ", " + iGridMatchCoeff[0][2] + "}"); System.out.println("iGridMatchCoeff[1]={" + iGridMatchCoeff[1][0] + ", " + iGridMatchCoeff[1][1] + ", " + iGridMatchCoeff[1][2] + "}"); System.out.println("worstGridMatchRotSkew()=" + IJ.d2s(worstGridMatchRotSkew(gridMatchCoeff), 5)); System.out .println("worstGridMatchTranslate()=" + IJ.d2s(worstGridMatchTranslate(gridMatchCoeff), 5)); } // hintGridTolerance==0 - do not try to determine shift from the hint (not // reliable yet) if (hintGridTolerance > 0) { if (worstGridMatchTranslate(gridMatchCoeff) <= hintGridTolerance) { // convert to pixels from // halfperiods (or just chnage // definition of // hintGridTolerance) if (global_debug_level > 1) System.out.println("worstGridMatchTranslate(gridMatchCoeff)= " + worstGridMatchTranslate(gridMatchCoeff) + ", hintGridTolerance=" + hintGridTolerance); iGridTranslateUV = new int[2]; iGridTranslateUV[0] = iGridMatchCoeff[0][2]; iGridTranslateUV[1] = iGridMatchCoeff[1][2]; } else { if (global_debug_level > 1) System.out.println( "*** Warning: combineGridCalibration() failed, worstGridMatchTranslate(gridMatchCoeff)= " + worstGridMatchTranslate(gridMatchCoeff) + ", hintGridTolerance=" + hintGridTolerance); return -1; } } if (global_debug_level > 0) { System.out.println( (((iGridMatchCoeff[0][2] + iGridMatchCoeff[1][2]) & 1) == 0) ? "EVEN shift" : "ODD shift"); } } else { if (global_debug_level > 0) System.out.println("*** Warning: combineGridCalibration(): gridMatchCoeff() failed"); return -1; } } if (((laserPointer != null) && (laserPointer.laserUVMap.length > 0)) || ((iGridTranslateUV != null) && (gridRotation >= 0))) { // no laser pointers, but hint grid with // specified tolerance if ((global_debug_level > 1) && (pointersXY == null)) System.out.println("This image does not contain any laser pointer data"); acalibrated = calibrateGrid( // now should work without laser pointers too laserPointer, pointersXY, removeOutOfGridPointers, gridRotation, iGridTranslateUV, noMessageBoxes, global_debug_level); if (global_debug_level > 1) { System.out.println("matchSimulatedPattern.laserCalibrateGrid() returned " + acalibrated + ((acalibrated > 0) ? " laser points used." : (((iGridTranslateUV == null) || (acalibrated < 0)) ? " - error code" : "none"))); } } if (global_debug_level > 0) System.out.println("Pattern size is " + getDArrayWidth() + " x " + getDArrayHeight()); return acalibrated; } // Modified 06/19 to move laser pointers to files public int combineGridCalibration(LaserPointer lp, // Only for possible hint on rotations/ flips.LaserPointer object // or null double[][] pointersXYUV, boolean removeOutOfGridPointers, // double[][][] hintGrid, // predicted grid array (or null) double hintGridTolerance, // allowed mismatch (fraction of period) or 0 - orientation only boolean invert, // for lwir int global_debug_level, // DEBUG_LEVEL boolean noMessageBoxes) { boolean has_lasers = false; if (pointersXYUV != null) { for (double[] e : pointersXYUV) if ((e != null) && (e.length > 2)) { has_lasers = true; break; } } int acalibrated = 0; double[][] gridMatchCoeff = null; double searchAround = 50; // 20.0; // how far to look for the grid node int gridRotation = -1; // undefined int[] iGridTranslateUV = null; // translate UV grid by these integer numbers if (hintGrid != null) { gridMatchCoeff = calcGridMatchMatrix(hintGrid, searchAround); // now already rounds rotate and re-replaces gridMatchCoeff with approximated, // refines gridMatchCoeff[0][2] and gridMatchCoeff[1][2] if (gridMatchCoeff != null) { gridRotation = matrixToRot(gridMatchCoeff); this.debugLevel = global_debug_level; int[][] iGridMatchCoeff = gridMatrixApproximate(gridMatchCoeff, invert); if (global_debug_level > 0) { System.out.println("gridMatchCoeff[0]={" + IJ.d2s(gridMatchCoeff[0][0], 5) + ", " + IJ.d2s(gridMatchCoeff[0][1], 5) + ", " + IJ.d2s(gridMatchCoeff[0][2], 5) + "}"); System.out.println("gridMatchCoeff[1]={" + IJ.d2s(gridMatchCoeff[1][0], 5) + ", " + IJ.d2s(gridMatchCoeff[1][1], 5) + ", " + IJ.d2s(gridMatchCoeff[1][2], 5) + "}"); System.out.println("gridRotation=" + gridRotation); System.out.println("iGridMatchCoeff[0]={" + iGridMatchCoeff[0][0] + ", " + iGridMatchCoeff[0][1] + ", " + iGridMatchCoeff[0][2] + "}"); System.out.println("iGridMatchCoeff[1]={" + iGridMatchCoeff[1][0] + ", " + iGridMatchCoeff[1][1] + ", " + iGridMatchCoeff[1][2] + "}"); System.out.println("worstGridMatchRotSkew()=" + IJ.d2s(worstGridMatchRotSkew(gridMatchCoeff, invert), 5)); System.out.println("worstGridMatchTranslate()=" + IJ.d2s(worstGridMatchTranslate(gridMatchCoeff, invert), 5)); } // hintGridTolerance==0 - do not try to determine shift from the hint (not // reliable yet) if (hintGridTolerance > 0) { if (worstGridMatchTranslate(gridMatchCoeff) <= hintGridTolerance) { // convert to pixels from // halfperiods (or just chnage // definition of // hintGridTolerance) if (global_debug_level > 0) System.out.println("worstGridMatchTranslate(gridMatchCoeff)= " + worstGridMatchTranslate(gridMatchCoeff) + ", hintGridTolerance=" + hintGridTolerance); iGridTranslateUV = new int[2]; iGridTranslateUV[0] = iGridMatchCoeff[0][2]; iGridTranslateUV[1] = iGridMatchCoeff[1][2]; } else { if (global_debug_level > 0) System.out.println( "*** Warning: combineGridCalibration() failed, worstGridMatchTranslate(gridMatchCoeff)= " + worstGridMatchTranslate(gridMatchCoeff) + ", hintGridTolerance=" + hintGridTolerance); return -1; } } if (global_debug_level > 0) { System.out.println( (((iGridMatchCoeff[0][2] + iGridMatchCoeff[1][2]) & 1) == 0) ? "EVEN shift" : "ODD shift"); // here } } else { if (global_debug_level > 0) System.out.println("*** Warning: combineGridCalibration(): gridMatchCoeff() failed"); return -1; } } if (has_lasers || ((iGridTranslateUV != null) && (gridRotation >= 0))) { // no laser pointers, but hint grid // with specified tolerance if ((global_debug_level > 1) && (pointersXYUV == null)) System.out.println("This image does not contain any laser pointer data"); double maxOffsetFromCenter = 0.6; // maximal offset of the laser spot from the center, relative to cell // radius boolean white_only = true; if (lp != null) { maxOffsetFromCenter = lp.maxOffsetFromCenter; white_only = lp.whiteOnly; } acalibrated = calibrateGrid( // now should work without laser pointers too lp, // for flips hint only white_only, maxOffsetFromCenter, pointersXYUV, removeOutOfGridPointers, gridRotation, iGridTranslateUV, noMessageBoxes, global_debug_level); if (global_debug_level > 1) { System.out.println("matchSimulatedPattern.laserCalibrateGrid() returned " + acalibrated + ((acalibrated > 0) ? " laser points used." : (((iGridTranslateUV == null) || (acalibrated < 0)) ? " - error code" : "none"))); } } if (global_debug_level > 0) System.out.println("Pattern size is " + getDArrayWidth() + " x " + getDArrayHeight()); return acalibrated; } public int replaceGridXYWithProjected(double[][][] projectedGrid, String debugTitle) { int minU = 0, minV = 0, maxU = 0, maxV = 0; boolean notYetSet = true; for (double[][] row : projectedGrid) for (double[] cell : row) if (cell != null) { int u = (int) cell[2]; int v = (int) cell[3]; if (notYetSet) { minU = u; maxU = u; minV = v; maxV = v; notYetSet = false; } else { if (minU > u) minU = u; if (maxU < u) maxU = u; if (minV > v) minV = v; if (maxV < v) maxV = v; } } double[][][] grid = new double[maxV - minV + 1][maxU - minU + 1][]; // for (double [][]row:grid) for (double [] cell:row) cell=null; // See if this // works with "enhanced for loop" for (double[][] row : grid) for (int u = 0; u < row.length; u++) row[u] = null; // See if this works with "enhanced for loop" for (double[][] row : projectedGrid) for (double[] cell : row) if (cell != null) { int u = (int) cell[2]; int v = (int) cell[3]; double[] xy = { cell[0], cell[1] }; grid[v - minV][u - minU] = xy; } int numNewDefined = 0; // System.out.println("this.PATTERN_GRID.length="+this.PATTERN_GRID.length+"this.PATTERN_GRID[0.length="+this.PATTERN_GRID[0].length); // System.out.println("this.targetUV.length="+this.targetUV.length+"this.targetUV[0.length="+this.targetUV[0].length); if ((debugTitle != null) && (this.debugLevel > 0)) { double[][] debugReplace = null; String[] debugTiltes = { "deltaX", "deltaY", "Contrast", "measX", "measY", "targetU", "targetV" }; debugReplace = new double[7][this.PATTERN_GRID.length * this.PATTERN_GRID[0].length]; for (int i = 0; i < debugReplace.length; i++) Arrays.fill(debugReplace[i], Double.NaN); for (int v = 0; v < this.PATTERN_GRID.length; v++) for (int u = 0; u < this.PATTERN_GRID[v].length; u++) { double[][] cell = this.PATTERN_GRID[v][u]; if ((cell != null) && (cell.length > 0) && (cell[0] != null) && (cell[0].length > 1)) { int tu = this.targetUV[v][u][0] - minU; int tv = this.targetUV[v][u][1] - minV; if ((tu >= 0) && (tv >= 0) && (tv < grid.length) && (tu < grid[tv].length) && (grid[tv][tu] != null)) { int index = v * this.PATTERN_GRID[0].length + u; debugReplace[0][index] = grid[tv][tu][0] - this.PATTERN_GRID[v][u][0][0]; debugReplace[1][index] = grid[tv][tu][1] - this.PATTERN_GRID[v][u][0][1]; debugReplace[2][index] = this.PATTERN_GRID[v][u][0][2]; debugReplace[3][index] = this.PATTERN_GRID[v][u][0][0]; debugReplace[4][index] = this.PATTERN_GRID[v][u][0][1]; debugReplace[5][index] = tu; debugReplace[6][index] = tu; } } } ShowDoubleFloatArrays.showArrays(debugReplace, this.PATTERN_GRID[0].length, this.PATTERN_GRID.length, true, "replaceGridXYWithProjected-" + debugTitle, debugTiltes); } for (int v = 0; v < this.PATTERN_GRID.length; v++) for (int u = 0; u < this.PATTERN_GRID[v].length; u++) { double[][] cell = this.PATTERN_GRID[v][u]; if ((cell != null) && (cell.length > 0) && (cell[0] != null) && (cell[0].length > 1)) { // System.out.print("v="+v+" u="+u); int tu = this.targetUV[v][u][0] - minU; int tv = this.targetUV[v][u][1] - minV; // System.out.println(" tv="+tv+" tu="+tu); if ((tu >= 0) && (tv >= 0) && (tv < grid.length) && (tu < grid[tv].length) && (grid[tv][tu] != null)) { cell[0][0] = grid[tv][tu][0]; // -81 -.-1 cell[0][1] = grid[tv][tu][1]; if (Double.isNaN(cell[0][0]) || Double.isNaN(cell[0][1])) { this.PATTERN_GRID[v][u] = null; // make it undefined } else { numNewDefined++; } } else { this.PATTERN_GRID[v][u] = null; // make it undefined } } } return numNewDefined; } /* * Get calibrated pattern as a 8-slice image (can be saved as TIFF) first slice * - pixel X or -1 for undefined second slice - pixel Y or -1 for undefined * third slice - target U (may be negative) fourth slice - target V (may be * negative) other slices - if present fifth slice - local grid contrast (looks * for 2 white and 2 blacks around) - can be used to filter sixth slice - red * intensity of the grid (averaged around the grid node) seventh slice - green * intensity of the grid (averaged around the grid node) eighth slice - blue * intensity of the grid (averaged around the grid node) */ public ImagePlus getCalibratedPatternAsImage(String title, int numUsedPointers) { if ((this.targetUV == null) || (this.pXYUV == null)) { // System.out.println("getCalibratedPatternAsImage(): this.targetUV="+((this.targetUV==null)?"null":"not null")+", this.pixelsUV="+((this.pXYUV==null)?"null":"not null")); // System.out.println("Using grid w/o absolute calibration."); unCalibrateGrid(); // return null; } int numSlices = (this.gridContrastBrightness == null) ? 4 : 8; float[][] pixels = new float[numSlices][getWidth() * getHeight()]; ImageStack stack = new ImageStack(getWidth(), getHeight()); int index = 0; for (int v = 0; v < getHeight(); v++) for (int u = 0; u < getWidth(); u++) { if ((this.targetUV[v][u] == null) || (this.pXYUV[v][u] == null) || (this.pXYUV[v][u][0] < 0.0) || (this.pXYUV[v][u][1] < 0.0)) { // disregard negative sensor pixels pixels[0][index] = -1.0f; pixels[1][index] = -1.0f; pixels[2][index] = 0.0f; pixels[3][index] = 0.0f; if (numSlices > 4) { pixels[4][index] = 0.0f; // contrast pixels[5][index] = -1.0f; // red, undefined pixels[6][index] = -1.0f; // green, undefined pixels[7][index] = -1.0f; // blue, undefined } } else { pixels[0][index] = (float) this.pXYUV[v][u][0]; pixels[1][index] = (float) this.pXYUV[v][u][1]; pixels[2][index] = this.targetUV[v][u][0]; pixels[3][index] = this.targetUV[v][u][1]; if (numSlices > 4) { pixels[4][index] = (float) this.gridContrastBrightness[0][v][u]; // grid contrast pixels[5][index] = (float) this.gridContrastBrightness[1][v][u]; // red pixels[6][index] = (float) this.gridContrastBrightness[2][v][u]; // green pixels[7][index] = (float) this.gridContrastBrightness[3][v][u]; // blue } } index++; } stack.addSlice("pixel-X", pixels[0]); stack.addSlice("pixel-Y", pixels[1]); stack.addSlice("target-U", pixels[2]); stack.addSlice("target-V", pixels[3]); if (numSlices > 4) { stack.addSlice("contrast", pixels[4]); stack.addSlice("red", pixels[5]); stack.addSlice("green", pixels[6]); stack.addSlice("blue", pixels[7]); } ImagePlus imp = new ImagePlus(title, stack); imp.setProperty("USED_POINTERS", ((numUsedPointers >= 0) ? numUsedPointers : 0) + ""); // System.out.println("getCalibratedPatternAsImage(): // numUsedPointers="+numUsedPointers+" // getProperty(\"USED_POINTERS\")="+imp.getProperty("USED_POINTERS")); return imp; } // searching for single-pixel errors (program bug) public ImagePlus getCalibratedPatternCurvatureAsImage(String title) { if ((this.targetUV == null) || (this.pXYUV == null)) { String msg = "this.targetUV=" + ((this.targetUV == null) ? "null" : "not null") + ", this.pixelsUV=" + ((this.pXYUV == null) ? "null" : "not null"); IJ.showMessage("Error", msg); throw new IllegalArgumentException(msg); } double[][][] curves = new double[this.targetUV.length][this.targetUV[0].length][2]; double[][][] diff_curves = new double[this.targetUV.length][this.targetUV[0].length][2]; boolean[][] mask_curves = new boolean[this.targetUV.length][this.targetUV[0].length]; boolean[][] mask_diff_curves = new boolean[this.targetUV.length][this.targetUV[0].length]; for (int v = 0; v < curves.length; v++) for (int u = 0; u < curves[0].length; u++) { curves[v][u][0] = 0.0; curves[v][u][1] = 0.0; diff_curves[v][u][0] = 0.0; diff_curves[v][u][1] = 0.0; mask_curves[v][u] = false; mask_diff_curves[v][u] = false; } ImageStack stack = new ImageStack(getWidth(), getHeight()); int[][] dirs = { { 0, 0 }, { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 }, { -1, -1 }, { -1, 1 }, { 1, -1 }, { 1, 1 } }; double[] weights = { 1.0, -0.15, -0.15, -.15, -.15, -0.1, -0.1, -0.1, -0.1 }; // first pass - calculate "curvature" - difference between pixel dx, dy values // and those average (weighted) of 8 neigbors for (int v = 1; v < getHeight() - 1; v++) for (int u = 1; u < getWidth() - 1; u++) { double[] avrg = { 0.0, 0.0 }; boolean valid = true; for (int d = 0; d < dirs.length; d++) { int u1 = u + dirs[d][0]; int v1 = v + dirs[d][1]; if ((this.targetUV[v1][u1] == null) || (this.pXYUV[v1][u1] == null) || (this.pXYUV[v1][u1][0] < 0.0) || (this.pXYUV[v1][u1][0] < 0.0)) { // disregard negative sensor pixels valid = false; break; } else { avrg[0] += weights[d] * this.pXYUV[v1][u1][0]; avrg[1] += weights[d] * this.pXYUV[v1][u1][1]; } } if (valid) { curves[v][u][0] = avrg[0]; curves[v][u][1] = avrg[1]; mask_curves[v][u] = true; } } // second pass - calculate difference between curvatives of each node and and // those average (weighted) of 8 neigbors // This will mostly eliminate the distortion shift, and can be used as a measure // of the "noise" for (int v = 2; v < getHeight() - 2; v++) for (int u = 2; u < getWidth() - 2; u++) { double[] avrg = { 0.0, 0.0 }; boolean valid = true; for (int d = 0; d < dirs.length; d++) { int u1 = u + dirs[d][0]; int v1 = v + dirs[d][1]; if (!mask_curves[v1][u1]) { valid = false; break; } else { avrg[0] += weights[d] * curves[v1][u1][0]; avrg[1] += weights[d] * curves[v1][u1][1]; } } if (valid) { diff_curves[v][u][0] = avrg[0]; diff_curves[v][u][1] = avrg[1]; mask_diff_curves[v][u] = true; } } float[][] pixels = new float[7][getWidth() * getHeight()]; int numPix = 0; double sum = 0.0; int index = 0; double curvature, diff_curvature; for (int v = 0; v < getHeight(); v++) for (int u = 0; u < getWidth(); u++) { if (mask_curves[v][u]) { curvature = Math.sqrt(curves[v][u][0] * curves[v][u][0] + curves[v][u][1] * curves[v][u][1]); pixels[0][index] = (float) curvature; pixels[1][index] = (float) curves[v][u][0]; pixels[2][index] = (float) curves[v][u][1]; if (mask_diff_curves[v][u]) { diff_curvature = Math.sqrt(diff_curves[v][u][0] * diff_curves[v][u][0] + diff_curves[v][u][1] * diff_curves[v][u][1]); pixels[3][index] = (float) diff_curvature; pixels[4][index] = (float) diff_curves[v][u][0]; pixels[5][index] = (float) diff_curves[v][u][1]; pixels[6][index] = 1.0f; sum += diff_curvature * diff_curvature; numPix++; } else { pixels[3][index] = 0.0f; pixels[4][index] = 0.0f; pixels[5][index] = 0.0f; pixels[6][index] = 0.0f; } } else { pixels[0][index] = 0.0f; pixels[1][index] = 0.0f; pixels[2][index] = 0.0f; } index++; } stack.addSlice("curvature", pixels[0]); stack.addSlice("X-diff", pixels[1]); stack.addSlice("Y-diff", pixels[2]); stack.addSlice("error", pixels[3]); stack.addSlice("X-error", pixels[4]); stack.addSlice("Y-error", pixels[5]); stack.addSlice("mask", pixels[6]); if (numPix > 0) { double rms = Math.sqrt(sum / numPix); String msg = "Deviation calculated for " + numPix + " grid nodes. RMS=" + rms; System.out.println(msg); IJ.showMessage(msg); } else { String msg = "Zero points to calculate deviation"; System.out.println(msg); IJ.showMessage(msg); } ImagePlus imp = new ImagePlus(title, stack); return imp; } public ImagePlus getCalibratedPatternAsImage(ImagePlus imp_src, String prefix, int numUsedPointers) { // ImagePlus imp_result=getCalibratedPatternAsImage("grid-"+imp_src.getTitle(), // numUsedPointers); ImagePlus imp_result = getCalibratedPatternAsImage(prefix + imp_src.getTitle(), numUsedPointers); if (imp_result == null) { System.out.println("getCalibratedPatternAsImage(): Grid is empty !"); return null; } // copy all the properties to the new image JP46_Reader_camera jp4_instance = new JP46_Reader_camera(false); jp4_instance.copyProperties(imp_src, imp_result); jp4_instance.encodeProperiesToInfo(imp_result); return imp_result; } public boolean[] rotToFlips(int rot) { boolean[][] rot2flips = { // swapUV,flipU,flipV for different rotations above { false, false, false }, { true, true, false }, { false, true, true }, { true, false, true }, { false, false, true }, { true, false, false }, { false, true, false }, { true, true, true } }; return rot2flips[rot]; } public int flipsToRot(boolean swapUV, boolean flipU, boolean flipV) { for (int i = 0; i < 8; i++) if ((rotToFlips(i)[0] == swapUV) && (rotToFlips(i)[1] == flipU) && (rotToFlips(i)[2] == flipV)) return i; return -1; // never } // // move elsewhere? /** * Create this.targetUV and this.pixelsUV for the grid that does not have any * laser pointer references */ public void unCalibrateGrid() { // calculate targetUV that maps PATTERN_GRID cells to the target (absolute) UV this.targetUV = new int[this.PATTERN_GRID.length][this.PATTERN_GRID[0].length][]; this.pXYUV = new double[this.PATTERN_GRID.length][this.PATTERN_GRID[0].length][]; // Or set it back to original 9do not touch, rotate/shift in the end? Arrays.fill(this.UVShiftRot, 0); for (int v = 0; v < this.PATTERN_GRID.length; v++) for (int u = 0; u < this.PATTERN_GRID[v].length; u++) { if ((this.PATTERN_GRID[v][u] == null) || (this.PATTERN_GRID[v][u][0] == null)) { this.targetUV[v][u] = null; this.pXYUV[v][u] = null; } else { this.targetUV[v][u] = new int[2]; this.targetUV[v][u][0] = u; this.targetUV[v][u][1] = v; this.pXYUV[v][u] = new double[2]; this.pXYUV[v][u][0] = PATTERN_GRID[v][u][0][0]; this.pXYUV[v][u][1] = PATTERN_GRID[v][u][0][1]; } } } // returns -1 - failure, otherwise - number of points used for calibration array // (move default orientation to laserPointer paramerters public int calibrateGrid(LaserPointer laserPointer, double[][] xy, // null and zero length OK boolean removeOutOfGridPointers, int hintRotation, // rotation (0..7) found from hintGrid, -1 - undefined int[] hintTranslateUV, // found from hintGrid: translate UV by this vector or null if undefined // double [][][] hintGrid, // predicted grid array (or null) - use just // direction // double hintGridTolerance, // alllowed mismatch (fraction of period) or 0 - // orientation only boolean noMessageBoxes, int debugLevel) { if (xy == null) xy = new double[0][]; invalidateCalibration(); double[][] uv = uvFromXY(xy, removeOutOfGridPointers ? 2.0 : -1); // if (uv==null) return -1; int numPointesLeft = 0; for (int i = 0; i < xy.length; i++) if ((xy[i] != null) && (uv[i] != null)) numPointesLeft++; if (debugLevel > 1) { int numRemoved = 0; for (int i = 0; i < xy.length; i++) if ((xy[i] != null) && (uv[i] == null)) numRemoved++; System.out.println( "Removed " + numRemoved + " out-of-grid pointers, " + numPointesLeft + " pointers remain."); } // Now remove pointers that are not on white cells if ((laserPointer != null) && laserPointer.whiteOnly) { int numBad = 0; for (int i = 0; i < uv.length; i++) if (uv[i] != null) { // Verify that laser spots are on the white cells (sum of uv is even) if ((((int) (Math.floor(uv[i][0]) + Math.floor(uv[i][1]))) & 1) != 0) { String msg = "Laser point " + i + " is not on the white pattern cell, and this check is enforced in the configuration"; System.out.println("Warning:" + msg); if (!noMessageBoxes) IJ.showMessage("Warning", msg); uv[i] = null; numBad++; continue; } } if (numBad > 0) { String msg = "Removed " + numBad + " pointers on black cells"; System.out.println("Warning:" + msg); } } // Later some pointers may be removed even if they are used to determine // orientation/shift. But that should not lead // to white/black confusion /* * 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) int [][] dfltShifts={ {0,0}, {0,1}, {0,0}, {1,0}, {0,1}, {0,0}, {1,0}, * {0,0}}; */ boolean[] possibleRotations = { true, true, true, true, true, true, true, true }; // If orientation is hinted, remove all other ones from the list of possible // ones if (hintRotation >= 0) { // defind from the hintGrid for (int i = 0; i < possibleRotations.length; i++) possibleRotations[i] = (i == hintRotation); } // boolean [] partialPossibleRotations=new boolean [possibleRotations.length]; boolean pairMatch, allMatch; int[] diffUVTable = new int[2]; // difference between points specified in the table int[] diffUVMeas = new int[2]; // measured difference (PATTERN_GRID U,V int[] rotUVTable = new int[2]; // rotated 'laser' coordinates difference (should match measured) int[] belongsToGoodPair = new int[uv.length]; int[] belongsToBadPair = new int[uv.length]; for (int i = 0; i < uv.length; i++) { belongsToGoodPair[i] = 0; belongsToBadPair[i] = 0; } // pass 0 - process good/bad pairs, do not disable directions if does not match // if at least 1 good pair exists - remove all that do not match // if no good pairs - remove all bad // second pass: if more than 1 good pair - should match all (or error) // TODO: When hinted position, remove far pointers before matching pairs for (int pass = 0; pass < 2; pass++) { for (int i = 0; i < uv.length; i++) if (uv[i] != null) for (int j = i + 1; j < uv.length; j++) if (uv[j] != null) { pairMatch = false; allMatch = false; diffUVTable[0] = (int) Math .round(laserPointer.laserUVMap[j][0] - laserPointer.laserUVMap[i][0]); // should not // get here // if uv is // {} diffUVTable[1] = (int) Math .round(laserPointer.laserUVMap[j][1] - laserPointer.laserUVMap[i][1]); diffUVMeas[0] = (int) Math.round(uv[j][0] - uv[i][0]); diffUVMeas[1] = (int) Math.round(uv[j][1] - uv[i][1]); // see which rotations are possible for this pair of points if (debugLevel > 2) { System.out.println("pass=" + pass + " i=" + i + " j=" + j); System.out.println("diffUVTable=[" + diffUVTable[0] + "," + diffUVTable[1] + "]"); System.out.println("diffUVMeas= [" + diffUVMeas[0] + "," + diffUVMeas[1] + "]"); } for (int dir = 0; dir < rotations.length; dir++) { rotUVTable[0] = rotations[dir][0][0] * diffUVTable[0] + rotations[dir][0][1] * diffUVTable[1]; rotUVTable[1] = rotations[dir][1][0] * diffUVTable[0] + rotations[dir][1][1] * diffUVTable[1]; if (debugLevel > 2) { System.out.println( dir + ": rotUVTable= [" + rotUVTable[0] + "," + rotUVTable[1] + "]"); } if ((rotUVTable[0] == diffUVMeas[0]) && (rotUVTable[1] == diffUVMeas[1])) { pairMatch = true; if (possibleRotations[dir]) allMatch = true; // this and hinted direction } else { if (pass > 0) { // do not disable rotation on the first pass possibleRotations[dir] = false; } } } // TODO: Find maximal number of matching pointers? if (pass == 0) { // if (allMatch) { if (pairMatch) { belongsToGoodPair[i]++; belongsToGoodPair[j]++; } else { belongsToBadPair[i]++; belongsToBadPair[j]++; } } else { // second pass if (!pairMatch) { String msg = "Laser points\n" + i + " [" + IJ.d2s(laserPointer.laserUVMap[i][0], 2) + ":" + IJ.d2s(laserPointer.laserUVMap[i][1], 2) + "] -> [" + IJ.d2s(uv[i][0], 2) + ":" + IJ.d2s(uv[i][1], 2) + "] and \n" + j + " [" + IJ.d2s(laserPointer.laserUVMap[j][0], 2) + ":" + IJ.d2s(laserPointer.laserUVMap[j][1], 2) + "] -> [" + IJ.d2s(uv[j][0], 2) + ":" + IJ.d2s(uv[j][1], 2) + "] do not match"; System.out.println(msg); if (!noMessageBoxes) IJ.showMessage("Error", msg); unCalibrateGrid(); return -2; } else if (!allMatch) { String msg = "Following laser pointers can not be mapped simultaneously:\n"; for (int k = 0; k <= j; k++) if (uv[k] != null) { msg += k + " [" + IJ.d2s(laserPointer.laserUVMap[k][0], 2) + ":" + IJ.d2s(laserPointer.laserUVMap[k][1], 2) + "] -> [" + IJ.d2s(uv[k][0], 2) + ":" + IJ.d2s(uv[k][1], 2) + "]\n"; } System.out.println(msg); if (!noMessageBoxes) IJ.showMessage("Error", msg); unCalibrateGrid(); return -1; } } } if (pass == 0) { int numInGood = 0; int numInBad = 0; for (int i = 0; i < uv.length; i++) { if (belongsToGoodPair[i] > 0) numInGood++; if (belongsToBadPair[i] > 0) numInBad++; } if (numInBad > 0) { if (numInGood == 0) { String msg = "No matching laser points pairs exist, and " + numInBad + " points do not match"; System.out.println(msg); if (!noMessageBoxes) IJ.showMessage("Error", msg); /// will report error on the second pass } else if (numInBad > 0) { String msg = "Matching laser points pair(s) exist(s), but other:"; for (int i = 0; i < uv.length; i++) if ((belongsToBadPair[i] > 0) && (belongsToGoodPair[i] == 0)) { msg += " #" + i + " (" + (i + 1) + " of " + uv.length + ")"; uv[i] = null; // remove it from consideration } msg += " do not match and will be removed."; System.out.println(msg); if (!noMessageBoxes) IJ.showMessage("Error", msg); } } } } // TODO: here at least some rotations match all points. If there ere more than // two - try to use closest to the default/previous int rotation = (laserPointer != null) ? (flipsToRot(laserPointer.swapUV, laserPointer.flipU, laserPointer.flipV)) : 0; if (!possibleRotations[rotation]) { // current rotation value defined by laserPointer.{swapUV,flipU,flipV} does // not match // find a new one (first - without mirroring) for (int i = 0; i < 8; i++) if (possibleRotations[(((rotation ^ i) & 4)) | ((rotation + i) & 3)]) { rotation = (((rotation ^ i) & 4)) | ((rotation + i) & 3); // first tried in the same half, then - // the next one break; } if (!possibleRotations[rotation]) { // Program bug - should not happen String msg = "Program error - could not find laser point mapping while it should exist\n"; System.out.println(msg); if (!noMessageBoxes) IJ.showMessage("Error", msg); unCalibrateGrid(); return -3; } } // now rotation is the correct one, update laserPointer.{swapUV,flipU,flipV}; if (laserPointer != null) { laserPointer.swapUV = rotToFlips(rotation)[0]; laserPointer.flipU = rotToFlips(rotation)[1]; laserPointer.flipV = rotToFlips(rotation)[2]; } // calculate shift int[] uvShift = dfltShifts[rotation].clone(); // {0,0}; for (int i = 0; i < uv.length; i++) if (uv[i] != null) { // laserPointer -> uv=={} uvShift[0] = (int) Math.round(uv[i][0] - (rotations[rotation][0][0] * laserPointer.laserUVMap[i][0] + rotations[rotation][0][1] * laserPointer.laserUVMap[i][1])); uvShift[1] = (int) Math.round(uv[i][1] - (rotations[rotation][1][0] * laserPointer.laserUVMap[i][0] + rotations[rotation][1][1] * laserPointer.laserUVMap[i][1])); break; } // Hinted shift will only be used if no laser pointers are available, otherwise // - only verify/warn if (hintTranslateUV != null) { // if ((uv.length==0) || (numPointesLeft==0)){ if (numPointesLeft == 0) { uvShift[0] = hintTranslateUV[0]; uvShift[1] = hintTranslateUV[1]; if (debugLevel > 1) { System.out.println("No laser pointers available, using hinted translation"); } } else { if ((uvShift[0] == hintTranslateUV[0]) && (uvShift[0] == hintTranslateUV[0])) { if (debugLevel > 1) { System.out.println("Translation from the laser pointers matches the hinted one"); } } else { if (debugLevel > 1) { System.out.println("Translation from the laser pointers does not match the hinted one:"); System.out.println("Hinted: delta U=" + hintTranslateUV[0] + ", V=" + hintTranslateUV[1]); System.out.println("Lasers: delta U=" + uvShift[0] + ", V=" + uvShift[1]); System.out.println("Trusting lasers"); } } } } // calculate remap array (rotation+translation) from the target UV to the // measured grid UV. this.reMap = new int[2][3]; // seems it is never used? this.reMap[0][0] = rotations[rotation][0][0]; this.reMap[0][1] = rotations[rotation][0][1]; this.reMap[0][2] = uvShift[0]; this.reMap[1][0] = rotations[rotation][1][0]; this.reMap[1][1] = rotations[rotation][1][1]; this.reMap[1][2] = uvShift[1]; // calculate reverse remap array (rotation+translation) from the the measured // grid UV to the target UV int reRot = (rotation >= 4) ? rotation : ((4 - rotation) & 3); // number of reverse mirror-rotation // int [] UVRot={uvShift[0],uvShift[1],reRot}; int[] UVRot = { -(rotations[reRot][0][0] * uvShift[0] + rotations[reRot][0][1] * uvShift[1]), -(rotations[reRot][1][0] * uvShift[0] + rotations[reRot][1][1] * uvShift[1]), reRot }; return applyUVShiftRot(UVRot, // int [] UVShiftRot, uv, // double [][]uv, laserPointer, noMessageBoxes); /* * int [][] reReMap={ {rotations[reRot][0][0],rotations[reRot][0][1], * -(rotations[reRot][0][0]*uvShift[0] +rotations[reRot][0][1]*uvShift[1])}, * {rotations[reRot][1][0],rotations[reRot][1][1], * -(rotations[reRot][1][0]*uvShift[0] +rotations[reRot][1][1]*uvShift[1])}}; * * if (debugLevel>1){ * System.out.println("rotation="+rotation+", reMap= [["+this.reMap[0][0]+","+ * this.reMap[0][1]+","+this.reMap[0][2]+"]["+ * +this.reMap[1][0]+","+this.reMap[1][1]+","+this.reMap[1][2]+"]]"); * System.out.println("reRot="+ * reRot+", reReMap= [["+reReMap[0][0]+","+reReMap[0][1]+","+reReMap[0][2]+ * "]["+ +reReMap[1][0]+","+reReMap[1][1]+","+reReMap[1][2]+"]]"); } // * calculate targetUV that maps PATTERN_GRID cells to the target (absolute) UV * this.targetUV=new int * [this.PATTERN_GRID.length][this.PATTERN_GRID[0].length][]; this.pXYUV=new * double [this.PATTERN_GRID.length][this.PATTERN_GRID[0].length][]; for (int * v=0;v<this.PATTERN_GRID.length;v++) for (int * u=0;u<this.PATTERN_GRID[v].length;u++){ if ((this.PATTERN_GRID[v][u]==null) * || (this.PATTERN_GRID[v][u][0]==null)) { this.targetUV[v][u]=null; * this.pXYUV[v][u]=null; } else { this.targetUV[v][u]=new int [2]; * this.targetUV[v][u][0]=reReMap[0][0]*u+reReMap[0][1]*v+reReMap[0][2]; * this.targetUV[v][u][1]=reReMap[1][0]*u+reReMap[1][1]*v+reReMap[1][2]; // * System.out.println("v="+v+", u="+u+", PATTERN_GRID.length="+PATTERN_GRID. * length+", PATTERN_GRID[v].length="+PATTERN_GRID[v].length); // * System.out.println("this.pixelsUV.length="+this.pixelsUV.length); // * System.out.println("this.pixelsUV["+v+"].length="+this.pixelsUV[v].length); * this.pXYUV[v][u]=new double [2]; * this.pXYUV[v][u][0]=PATTERN_GRID[v][u][0][0]; * this.pXYUV[v][u][1]=PATTERN_GRID[v][u][0][1]; } } int numGood=0; int * numBad=0; double [] distUV=new double[2]; double dist; for (int * i=0;i<uv.length;i++) if (uv[i]!=null) { //laserPointer == null > uv={} // * Verify that laser spots are inside specified distance from the cell centers * distUV[0]=reReMap[0][0]*uv[i][0]+reReMap[0][1]*uv[i][1]+reReMap[0][2]- * laserPointer.laserUVMap[i][0]; * distUV[1]=reReMap[1][0]*uv[i][0]+reReMap[1][1]*uv[i][1]+reReMap[1][2]- * laserPointer.laserUVMap[i][1]; * dist=Math.sqrt(distUV[0]*distUV[0]+distUV[1]*distUV[1]); if (debugLevel>1){ * System.out.println("Laser spot #"+i+", distance from predicted ="+ * IJ.d2s(dist,3)+" ("+IJ.d2s(200*dist,3)+ * "% of cell radius), du="+IJ.d2s(distUV[0],3)+", dv="+IJ.d2s(distUV[1],3)); } * if ((2*dist)> laserPointer.maxOffsetFromCenter){ String * msg="Laser point "+(i+1)+"(of "+uv. * length+") is too far from the specified location, and this check is enforced in the configuration\n" * + "measured distance is "+ * IJ.d2s(200*dist,1)+"% of the cell radius, specified is "+ * IJ.d2s(100*laserPointer.maxOffsetFromCenter,1)+"%"; * System.out.println("Warning:"+msg); if (!noMessageBoxes) * IJ.showMessage("Warning",msg); numBad++; uv[i]=null; continue; } numGood++; } * if ((debugLevel>0) && (numBad>0)){ System.out.println("Removed " * +numBad+" pointers that are too far from the predicted locations"); } return * numGood; */ } public int calibrateGrid(LaserPointer lp, // only as hint for rotations/flips (may update), null is OK boolean white_only, // laser pointer only on white double maxOffsetFromCenter, double[][] xyuv, // null and zero length OK now combines x,y and // laserPointer.laserUVMap u,v // each non- null xyuv[] should be either 2 or 4 long boolean removeOutOfGridPointers, int hintRotation, // rotation (0..7) found from hintGrid, -1 - undefined int[] hintTranslateUV, // found from hintGrid: translate UV by this vector or null if undefined // double [][][] hintGrid, // predicted grid array (or null) - use just // direction // double hintGridTolerance, // alllowed mismatch (fraction of period) or 0 - // orientation only boolean noMessageBoxes, int debugLevel) { if (xyuv == null) xyuv = new double[0][]; invalidateCalibration(); boolean has_lasers = false; for (double[] e : xyuv) if ((e != null) && (e.length > 2)) { has_lasers = true; break; } double[][] uv = uvFromXY(xyuv, removeOutOfGridPointers ? 2.0 : -1); // if (uv==null) return -1; int numPointesLeft = 0; for (int i = 0; i < xyuv.length; i++) if ((xyuv[i] != null) && (uv[i] != null)) numPointesLeft++; if (debugLevel > 1) { int numRemoved = 0; for (int i = 0; i < xyuv.length; i++) if ((xyuv[i] != null) && (uv[i] == null)) numRemoved++; System.out.println( "Removed " + numRemoved + " out-of-grid pointers, " + numPointesLeft + " pointers remain."); } // Now remove pointers that are not on white cells if (has_lasers && white_only) { int numBad = 0; for (int i = 0; i < uv.length; i++) if (uv[i] != null) { // Verify that laser spots are on the white cells (sum of uv is even) if ((((int) (Math.floor(uv[i][0]) + Math.floor(uv[i][1]))) & 1) != 0) { String msg = "Laser point " + i + " is not on the white pattern cell, and this check is enforced in the configuration"; System.out.println("Warning:" + msg); if (!noMessageBoxes) IJ.showMessage("Warning", msg); uv[i] = null; numBad++; continue; } } if (numBad > 0) { String msg = "Removed " + numBad + " pointers on black cells"; System.out.println("Warning:" + msg); } } // Later some pointers may be removed even if they are used to determine // orientation/shift. But that should not lead // to white/black confusion /* * 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) int [][] dfltShifts={ {0,0}, {0,1}, {0,0}, {1,0}, {0,1}, {0,0}, {1,0}, * {0,0}}; */ boolean[] possibleRotations = { true, true, true, true, true, true, true, true }; // If orientation is hinted, remove all other ones from the list of possible // ones if (hintRotation >= 0) { // defind from the hintGrid for (int i = 0; i < possibleRotations.length; i++) possibleRotations[i] = (i == hintRotation); } // boolean [] partialPossibleRotations=new boolean [possibleRotations.length]; boolean pairMatch, allMatch; int[] diffUVTable = new int[2]; // difference between points specified in the table int[] diffUVMeas = new int[2]; // measured difference (PATTERN_GRID U,V int[] rotUVTable = new int[2]; // rotated 'laser' coordinates difference (should match measured) int[] belongsToGoodPair = new int[uv.length]; int[] belongsToBadPair = new int[uv.length]; for (int i = 0; i < uv.length; i++) { belongsToGoodPair[i] = 0; belongsToBadPair[i] = 0; } // pass 0 - process good/bad pairs, do not disable directions if does not match // if at least 1 good pair exists - remove all that do not match // if no good pairs - remove all bad // second pass: if more than 1 good pair - should match all (or error) // TODO: When hinted position, remove far pointers before matching pairs for (int pass = 0; pass < 2; pass++) { for (int i = 0; i < uv.length; i++) if (uv[i] != null) for (int j = i + 1; j < uv.length; j++) if (uv[j] != null) { // xyuv[i] != null, xyuv[j] != null pairMatch = false; allMatch = false; diffUVTable[0] = (int) Math.round(xyuv[j][2] - xyuv[i][2]); // should not get here if uv is // {} diffUVTable[1] = (int) Math.round(xyuv[j][3] - xyuv[i][3]); diffUVMeas[0] = (int) Math.round(uv[j][0] - uv[i][0]); diffUVMeas[1] = (int) Math.round(uv[j][1] - uv[i][1]); // see which rotations are possible for this pair of points if (debugLevel > 2) { System.out.println("pass=" + pass + " i=" + i + " j=" + j); System.out.println("diffUVTable=[" + diffUVTable[0] + "," + diffUVTable[1] + "]"); System.out.println("diffUVMeas= [" + diffUVMeas[0] + "," + diffUVMeas[1] + "]"); } for (int dir = 0; dir < rotations.length; dir++) { rotUVTable[0] = rotations[dir][0][0] * diffUVTable[0] + rotations[dir][0][1] * diffUVTable[1]; rotUVTable[1] = rotations[dir][1][0] * diffUVTable[0] + rotations[dir][1][1] * diffUVTable[1]; if (debugLevel > 2) { System.out.println( dir + ": rotUVTable= [" + rotUVTable[0] + "," + rotUVTable[1] + "]"); } if ((rotUVTable[0] == diffUVMeas[0]) && (rotUVTable[1] == diffUVMeas[1])) { pairMatch = true; if (possibleRotations[dir]) allMatch = true; // this and hinted direction } else { if (pass > 0) { // do not disable rotation on the first pass possibleRotations[dir] = false; } } } // TODO: Find maximal number of matching pointers? if (pass == 0) { // if (allMatch) { if (pairMatch) { belongsToGoodPair[i]++; belongsToGoodPair[j]++; } else { belongsToBadPair[i]++; belongsToBadPair[j]++; } } else { // second pass if (!pairMatch) { String msg = "Laser points\n" + i + " [" + IJ.d2s(xyuv[i][2], 2) + ":" + IJ.d2s(xyuv[i][3], 2) + "] -> [" + IJ.d2s(uv[i][0], 2) + ":" + IJ.d2s(uv[i][1], 2) + "] and \n" + j + " [" + IJ.d2s(xyuv[j][2], 2) + ":" + IJ.d2s(xyuv[j][3], 2) + "] -> [" + IJ.d2s(uv[j][0], 2) + ":" + IJ.d2s(uv[j][1], 2) + "] do not match"; System.out.println(msg); if (!noMessageBoxes) IJ.showMessage("Error", msg); unCalibrateGrid(); return -2; } else if (!allMatch) { String msg = "Following laser pointers can not be mapped simultaneously:\n"; for (int k = 0; k <= j; k++) if (uv[k] != null) { msg += k + " [" + IJ.d2s(xyuv[k][2], 2) + ":" + IJ.d2s(xyuv[k][3], 2) + "] -> [" + IJ.d2s(uv[k][0], 2) + ":" + IJ.d2s(uv[k][1], 2) + "]\n"; } System.out.println(msg); if (!noMessageBoxes) IJ.showMessage("Error", msg); unCalibrateGrid(); return -1; } } } if (pass == 0) { int numInGood = 0; int numInBad = 0; for (int i = 0; i < uv.length; i++) { if (belongsToGoodPair[i] > 0) numInGood++; if (belongsToBadPair[i] > 0) numInBad++; } if (numInBad > 0) { if (numInGood == 0) { String msg = "No matching laser points pairs exist, and " + numInBad + " points do not match"; System.out.println(msg); if (!noMessageBoxes) IJ.showMessage("Error", msg); /// will report error on the second pass } else if (numInBad > 0) { String msg = "Matching laser points pair(s) exist(s), but other:"; for (int i = 0; i < uv.length; i++) if ((belongsToBadPair[i] > 0) && (belongsToGoodPair[i] == 0)) { msg += " #" + i + " (" + (i + 1) + " of " + uv.length + ")"; uv[i] = null; // remove it from consideration } msg += " do not match and will be removed."; System.out.println(msg); if (!noMessageBoxes) IJ.showMessage("Error", msg); } } } } // TODO: here at least some rotations match all points. If there ere more than // two - try to use closest to the default/previous int rotation = (lp != null) ? (flipsToRot(lp.swapUV, lp.flipU, lp.flipV)) : 0; if (!possibleRotations[rotation]) { // current rotation value defined by laserPointer.{swapUV,flipU,flipV} does // not match // find a new one (first - without mirroring) for (int i = 0; i < 8; i++) if (possibleRotations[(((rotation ^ i) & 4)) | ((rotation + i) & 3)]) { rotation = (((rotation ^ i) & 4)) | ((rotation + i) & 3); // first tried in the same half, then - // the next one break; } if (!possibleRotations[rotation]) { // Program bug - should not happen String msg = "Program error - could not find laser point mapping while it should exist\n"; System.out.println(msg); if (!noMessageBoxes) IJ.showMessage("Error", msg); unCalibrateGrid(); return -3; } } // now rotation is the correct one, update laserPointer.{swapUV,flipU,flipV}; if (lp != null) { lp.swapUV = rotToFlips(rotation)[0]; lp.flipU = rotToFlips(rotation)[1]; lp.flipV = rotToFlips(rotation)[2]; } // calculate shift int[] uvShift = dfltShifts[rotation].clone(); // {0,0}; for (int i = 0; i < uv.length; i++) if (uv[i] != null) { // laserPointer -> uv=={} uvShift[0] = (int) Math.round( uv[i][0] - (rotations[rotation][0][0] * xyuv[i][2] + rotations[rotation][0][1] * xyuv[i][3])); uvShift[1] = (int) Math.round( uv[i][1] - (rotations[rotation][1][0] * xyuv[i][2] + rotations[rotation][1][1] * xyuv[i][3])); break; } // Hinted shift will only be used if no laser pointers are available, otherwise // - only verify/warn if (hintTranslateUV != null) { // if ((uv.length==0) || (numPointesLeft==0)){ if (numPointesLeft == 0) { uvShift[0] = hintTranslateUV[0]; uvShift[1] = hintTranslateUV[1]; if (debugLevel > 1) { System.out.println("No laser pointers available, using hinted translation"); } } else { if ((uvShift[0] == hintTranslateUV[0]) && (uvShift[0] == hintTranslateUV[0])) { if (debugLevel > 1) { System.out.println("Translation from the laser pointers matches the hinted one"); } } else { if (debugLevel > 1) { System.out.println("Translation from the laser pointers does not match the hinted one:"); System.out.println("Hinted: delta U=" + hintTranslateUV[0] + ", V=" + hintTranslateUV[1]); System.out.println("Lasers: delta U=" + uvShift[0] + ", V=" + uvShift[1]); System.out.println("Trusting lasers"); } } } } // calculate remap array (rotation+translation) from the target UV to the // measured grid UV. this.reMap = new int[2][3]; // seems it is never used? this.reMap[0][0] = rotations[rotation][0][0]; this.reMap[0][1] = rotations[rotation][0][1]; this.reMap[0][2] = uvShift[0]; this.reMap[1][0] = rotations[rotation][1][0]; this.reMap[1][1] = rotations[rotation][1][1]; this.reMap[1][2] = uvShift[1]; // calculate reverse remap array (rotation+translation) from the the measured // grid UV to the target UV int reRot = (rotation >= 4) ? rotation : ((4 - rotation) & 3); // number of reverse mirror-rotation // int [] UVRot={uvShift[0],uvShift[1],reRot}; int[] UVRot = { -(rotations[reRot][0][0] * uvShift[0] + rotations[reRot][0][1] * uvShift[1]), -(rotations[reRot][1][0] * uvShift[0] + rotations[reRot][1][1] * uvShift[1]), reRot }; // return applyUVShiftRot( // UVRot, // int [] UVShiftRot, // uv, // double [][]uv, // laserPointer, // noMessageBoxes); return applyUVShiftRot(UVRot, // int [] UVShiftRot, uv, // double [][] uv, xyuv, // double [][] xyuv, // [][2], [][3] contain laser pointers u,v maxOffsetFromCenter, // double maxOffsetFromCenter, // LaserPointer laserPointer, noMessageBoxes); } public int applyUVShiftRot(int[] UVShiftRot, double[][] uv, LaserPointer laserPointer, boolean noMessageBoxes) { if (UVShiftRot != null) this.UVShiftRot = UVShiftRot.clone(); int[][] reReMap = getRemapMatrix(UVShiftRot); if (debugLevel > 1) { // System.out.println("rotation="+rotation+", reMap= // [["+this.reMap[0][0]+","+this.reMap[0][1]+","+this.reMap[0][2]+"]["+ // +this.reMap[1][0]+","+this.reMap[1][1]+","+this.reMap[1][2]+"]]"); System.out.println("reRot=" + UVShiftRot[2] + ", reReMap= [[" + reReMap[0][0] + "," + reReMap[0][1] + "," + reReMap[0][2] + "][" + +reReMap[1][0] + "," + reReMap[1][1] + "," + reReMap[1][2] + "]]"); } // calculate targetUV that maps PATTERN_GRID cells to the target (absolute) UV this.targetUV = new int[this.PATTERN_GRID.length][this.PATTERN_GRID[0].length][]; this.pXYUV = new double[this.PATTERN_GRID.length][this.PATTERN_GRID[0].length][]; for (int v = 0; v < this.PATTERN_GRID.length; v++) for (int u = 0; u < this.PATTERN_GRID[v].length; u++) { if ((this.PATTERN_GRID[v][u] == null) || (this.PATTERN_GRID[v][u][0] == null)) { this.targetUV[v][u] = null; this.pXYUV[v][u] = null; } else { this.targetUV[v][u] = new int[2]; this.targetUV[v][u][0] = reReMap[0][0] * u + reReMap[0][1] * v + reReMap[0][2]; this.targetUV[v][u][1] = reReMap[1][0] * u + reReMap[1][1] * v + reReMap[1][2]; // System.out.println("v="+v+", u="+u+", // PATTERN_GRID.length="+PATTERN_GRID.length+", // PATTERN_GRID[v].length="+PATTERN_GRID[v].length); // System.out.println("this.pixelsUV.length="+this.pixelsUV.length); // System.out.println("this.pixelsUV["+v+"].length="+this.pixelsUV[v].length); this.pXYUV[v][u] = new double[2]; this.pXYUV[v][u][0] = PATTERN_GRID[v][u][0][0]; this.pXYUV[v][u][1] = PATTERN_GRID[v][u][0][1]; } } int numGood = 0; int numBad = 0; double[] distUV = new double[2]; double dist; if (laserPointer != null) { for (int i = 0; i < uv.length; i++) if (uv[i] != null) { // laserPointer == null > uv={} // Verify that laser spots are inside specified distance from the cell centers distUV[0] = reReMap[0][0] * uv[i][0] + reReMap[0][1] * uv[i][1] + reReMap[0][2] - laserPointer.laserUVMap[i][0]; distUV[1] = reReMap[1][0] * uv[i][0] + reReMap[1][1] * uv[i][1] + reReMap[1][2] - laserPointer.laserUVMap[i][1]; dist = Math.sqrt(distUV[0] * distUV[0] + distUV[1] * distUV[1]); if (debugLevel > 1) { System.out.println("Laser spot #" + i + ", distance from predicted =" + IJ.d2s(dist, 3) + " (" + IJ.d2s(200 * dist, 3) + "% of cell radius), du=" + IJ.d2s(distUV[0], 3) + ", dv=" + IJ.d2s(distUV[1], 3)); } if ((2 * dist) > laserPointer.maxOffsetFromCenter) { String msg = "Laser point " + (i + 1) + "(of " + uv.length + ") is too far from the specified location, and this check is enforced in the configuration\n" + "measured distance is " + IJ.d2s(200 * dist, 1) + "% of the cell radius, specified is " + IJ.d2s(100 * laserPointer.maxOffsetFromCenter, 1) + "%"; System.out.println("Warning:" + msg); if (!noMessageBoxes) IJ.showMessage("Warning", msg); numBad++; uv[i] = null; continue; } numGood++; } } if ((debugLevel > 0) && (numBad > 0)) { System.out.println("Removed " + numBad + " pointers that are too far from the predicted locations"); } return numGood; } public int applyUVShiftRot(int[] UVShiftRot, double[][] uv, double[][] xyuv, // [][2], [][3] contain laser pointers // u,v double maxOffsetFromCenter, // LaserPointer laserPointer, boolean noMessageBoxes) { if (UVShiftRot != null) this.UVShiftRot = UVShiftRot.clone(); boolean has_lasers = false; for (double[] e : xyuv) if ((e != null) && (e.length > 2)) { has_lasers = true; break; } int[][] reReMap = getRemapMatrix(UVShiftRot); if (debugLevel > 1) { // System.out.println("rotation="+rotation+", reMap= // [["+this.reMap[0][0]+","+this.reMap[0][1]+","+this.reMap[0][2]+"]["+ // +this.reMap[1][0]+","+this.reMap[1][1]+","+this.reMap[1][2]+"]]"); System.out.println("reRot=" + UVShiftRot[2] + ", reReMap= [[" + reReMap[0][0] + "," + reReMap[0][1] + "," + reReMap[0][2] + "][" + +reReMap[1][0] + "," + reReMap[1][1] + "," + reReMap[1][2] + "]]"); } // calculate targetUV that maps PATTERN_GRID cells to the target (absolute) UV this.targetUV = new int[this.PATTERN_GRID.length][this.PATTERN_GRID[0].length][]; this.pXYUV = new double[this.PATTERN_GRID.length][this.PATTERN_GRID[0].length][]; for (int v = 0; v < this.PATTERN_GRID.length; v++) for (int u = 0; u < this.PATTERN_GRID[v].length; u++) { if ((this.PATTERN_GRID[v][u] == null) || (this.PATTERN_GRID[v][u][0] == null)) { this.targetUV[v][u] = null; this.pXYUV[v][u] = null; } else { this.targetUV[v][u] = new int[2]; this.targetUV[v][u][0] = reReMap[0][0] * u + reReMap[0][1] * v + reReMap[0][2]; this.targetUV[v][u][1] = reReMap[1][0] * u + reReMap[1][1] * v + reReMap[1][2]; // System.out.println("v="+v+", u="+u+", // PATTERN_GRID.length="+PATTERN_GRID.length+", // PATTERN_GRID[v].length="+PATTERN_GRID[v].length); // System.out.println("this.pixelsUV.length="+this.pixelsUV.length); // System.out.println("this.pixelsUV["+v+"].length="+this.pixelsUV[v].length); this.pXYUV[v][u] = new double[2]; this.pXYUV[v][u][0] = PATTERN_GRID[v][u][0][0]; this.pXYUV[v][u][1] = PATTERN_GRID[v][u][0][1]; } } int numGood = 0; int numBad = 0; double[] distUV = new double[2]; double dist; if (has_lasers) { for (int i = 0; i < uv.length; i++) if (uv[i] != null) { // laserPointer == null > uv={} // Verify that laser spots are inside specified distance from the cell centers distUV[0] = reReMap[0][0] * uv[i][0] + reReMap[0][1] * uv[i][1] + reReMap[0][2] - xyuv[i][2]; distUV[1] = reReMap[1][0] * uv[i][0] + reReMap[1][1] * uv[i][1] + reReMap[1][2] - xyuv[i][3]; dist = Math.sqrt(distUV[0] * distUV[0] + distUV[1] * distUV[1]); if (debugLevel > 0) { // 1) { System.out.println("Laser spot #" + i + ", distance from predicted =" + IJ.d2s(dist, 3) + " (" + IJ.d2s(200 * dist, 3) + "% of cell radius), du=" + IJ.d2s(distUV[0], 3) + ", dv=" + IJ.d2s(distUV[1], 3)); } if ((2 * dist) > maxOffsetFromCenter) { String msg = "Laser point " + (i + 1) + "(of " + uv.length + ") is too far from the specified location, and this check is enforced in the configuration\n" + "measured distance is " + IJ.d2s(200 * dist, 1) + "% of the cell radius, specified is " + IJ.d2s(100 * maxOffsetFromCenter, 1) + "%"; System.out.println("Warning:" + msg); if (!noMessageBoxes) IJ.showMessage("Warning", msg); numBad++; uv[i] = null; continue; } numGood++; } } if ((debugLevel > 0) && (numBad > 0)) { System.out.println("Removed " + numBad + " pointers that are too far from the predicted locations"); } return numGood; } /** * Rotate/flip PATTERN_GRID to match expected * * @param hintGrid [v][u][0 - pixel X, 1 - pixel Y, 2 - targetU, 3 - targetV * @return true if possible, false - if not */ /* * public boolean applyHintToGrid(double [][][] hintGrid){ * * } */ /** * Calculate grid fractional UV from x,y and grid array using bi-linear * interpolation from the nearest points. Iterates through all grid points, so * it is not optimal for processing each pixel. */ public double[][] uvFromXY(double[][] xy, double maxDist) { if (xy == null) { double[][] uv = new double[0][]; return uv; } double[][] uv = new double[xy.length][]; for (int i = 0; i < xy.length; i++) { uv[i] = uvFromXY(xy[i], maxDist); // if (uv[i]==null) return null; } return uv; } public double[] uvFromXY(double[] xy, double maxDist) { // double [][][][] grid= this.PATTERN_GRID; // double [] gXY=new double [2]; // int [] iUV=new int [2]; if (xy == null) return null; int[][] iUV = new int[3][2]; int width = this.PATTERN_GRID[0].length; // find closest point to xy double dist2, dx, dy, minDist2 = -1.0; double[] dist2Array = new double[this.PATTERN_GRID.length * width]; for (int v = 0; v < this.PATTERN_GRID.length; v++) for (int u = 0; u < this.PATTERN_GRID[v].length; u++) if ((this.PATTERN_GRID[v][u] != null) && (this.PATTERN_GRID[v][u][0] != null)) { dx = this.PATTERN_GRID[v][u][0][0] - xy[0]; dy = this.PATTERN_GRID[v][u][0][1] - xy[1]; dist2 = dx * dx + dy * dy; dist2Array[v * width + u] = dist2; if ((minDist2 < 0.0) || (minDist2 > dist2)) { minDist2 = dist2; iUV[0][0] = u; iUV[0][1] = v; } } else { dist2Array[v * width + u] = -1.0; } // now find two other closest points (not on the same line dist2Array[iUV[0][1] * width + iUV[0][0]] = -1.0; // mark used point int indx = 0; minDist2 = -1.0; for (int i = 0; i < dist2Array.length; i++) if ((dist2Array[i] >= 0.0) && ((minDist2 < 0.0) || (minDist2 > dist2Array[i]))) { minDist2 = dist2Array[i]; indx = i; } iUV[1][0] = indx % width; iUV[1][1] = indx / width; // mark all points on the same line as iUV[0] and iUV[1] // find closest of the remaining points indx = 0; minDist2 = -1.0; int dU1 = iUV[1][0] - iUV[0][0]; int dV1 = iUV[1][1] - iUV[0][1]; int dU2, dV2; for (int i = 0; i < dist2Array.length; i++) if ((dist2Array[i] >= 0.0) && ((minDist2 < 0.0) || (minDist2 > dist2Array[i]))) { dU2 = i % width - iUV[0][0]; dV2 = i / width - iUV[0][1]; if (dU1 * dV2 != dV1 * dU2) { minDist2 = dist2Array[i]; indx = i; } } iUV[2][0] = indx % width; iUV[2][1] = indx / width; // now there are 3 (not co-linear) points to interpolate u,v double[][] aMuv = { { iUV[1][0] - iUV[0][0], iUV[2][0] - iUV[0][0] }, { iUV[1][1] - iUV[0][1], iUV[2][1] - iUV[0][1] } }; Matrix Muv = new Matrix(aMuv); if ((this.PATTERN_GRID == null) || (this.PATTERN_GRID[iUV[0][1]][iUV[0][0]] == null) || (this.PATTERN_GRID[iUV[1][1]][iUV[1][0]] == null) || (this.PATTERN_GRID[iUV[2][1]][iUV[2][0]] == null) || (this.PATTERN_GRID[iUV[0][1]][iUV[0][0]][0] == null) || (this.PATTERN_GRID[iUV[1][1]][iUV[1][0]][0] == null) || (this.PATTERN_GRID[iUV[2][1]][iUV[2][0]][0] == null)) return null; double[][] aMxy = { { this.PATTERN_GRID[iUV[1][1]][iUV[1][0]][0][0] - this.PATTERN_GRID[iUV[0][1]][iUV[0][0]][0][0], this.PATTERN_GRID[iUV[2][1]][iUV[2][0]][0][0] - this.PATTERN_GRID[iUV[0][1]][iUV[0][0]][0][0] }, { this.PATTERN_GRID[iUV[1][1]][iUV[1][0]][0][1] - this.PATTERN_GRID[iUV[0][1]][iUV[0][0]][0][1], this.PATTERN_GRID[iUV[2][1]][iUV[2][0]][0][1] - this.PATTERN_GRID[iUV[0][1]][iUV[0][0]][0][1] } }; Matrix Mxy = new Matrix(aMxy); double[][] aVxy = { { xy[0] - this.PATTERN_GRID[iUV[0][1]][iUV[0][0]][0][0] }, { xy[1] - this.PATTERN_GRID[iUV[0][1]][iUV[0][0]][0][1] } }; Matrix Vxy = new Matrix(aVxy); double[][] aVuv0 = { { iUV[0][0] }, { iUV[0][1] } }; Matrix Vuv0 = new Matrix(aVuv0); Matrix Vuv = Vuv0.plus(Muv.times(Mxy.inverse()).times(Vxy)); double[] result = Vuv.getRowPackedCopy(); if (this.debugLevel > 1) System.out.println("X=" + IJ.d2s(xy[0], 3) + " Y=" + IJ.d2s(xy[1], 3)); if (this.debugLevel > 2) System.out.println(" " + "Grid[" + iUV[0][1] + "][" + iUV[0][0] + "]X=" + IJ.d2s(this.PATTERN_GRID[iUV[0][1]][iUV[0][0]][0][0], 3) + " " + "Grid[" + iUV[0][1] + "][" + iUV[0][0] + "]Y=" + IJ.d2s(this.PATTERN_GRID[iUV[0][1]][iUV[0][0]][0][1], 3) + "\n " + "Grid[" + iUV[1][1] + "][" + iUV[1][0] + "]X=" + IJ.d2s(this.PATTERN_GRID[iUV[1][1]][iUV[1][0]][0][0], 3) + " " + "Grid[" + iUV[1][1] + "][" + iUV[1][0] + "]Y=" + IJ.d2s(this.PATTERN_GRID[iUV[1][1]][iUV[1][0]][0][1], 3) + "\n " + "Grid[" + iUV[2][1] + "][" + iUV[2][0] + "]X=" + IJ.d2s(this.PATTERN_GRID[iUV[2][1]][iUV[2][0]][0][0], 3) + " " + "Grid[" + iUV[2][1] + "][" + iUV[2][0] + "]Y=" + IJ.d2s(this.PATTERN_GRID[iUV[2][1]][iUV[2][0]][0][1], 3)); if (this.debugLevel > 1) System.out.println("U=" + IJ.d2s(result[0], 3) + " V=" + IJ.d2s(result[1], 3) + "\n"); minDist2 = (result[0] - iUV[0][0]) * (result[0] - iUV[0][0]) + (result[1] - iUV[0][1]) * (result[1] - iUV[0][1]); if ((maxDist > 0.0) && (minDist2 > maxDist * maxDist)) { if (this.debugLevel > 0) System.out.println("minDist2=" + minDist2 + " (maxDist=" + maxDist + ") - pointer too far (x=" + xy[0] + " y=" + xy[1] + ")"); return null; // pointer too far from the grid (outside of the grid) } // change test (make sure that all 4 grid points around the result are defined int uFloor = (int) Math.floor(result[0]); int vFloor = (int) Math.floor(result[1]); int extra = (int) Math.round(maxDist) - 1; if (extra < 0) extra = 0; for (int v = vFloor - extra; v <= vFloor + extra + 1; v++) for (int u = uFloor - extra; u <= uFloor + extra + 1; u++) if ((v < 0) || (u < 0) || (v >= this.PATTERN_GRID.length) || (u >= this.PATTERN_GRID[v].length) || (this.PATTERN_GRID[v][u] == null) || (this.PATTERN_GRID[v][u][0] == null)) { if (this.debugLevel > 1) System.out.println("pointer=" + result[0] + ":" + result[1] + ", no grid at " + u + ":" + v + " - pointer does not have grid around (x=" + xy[0] + " y=" + xy[1] + "), extra=" + extra + " vFloor=" + vFloor + " uFloor=" + uFloor); for (int iiv = -2; iiv < 3; iiv++) { for (int iiu = -2; iiu < 3; iiu++) { boolean iinValid = ((iiv + vFloor) < 0) || ((iiv + vFloor) >= this.PATTERN_GRID.length) || ((iiu + uFloor) < 0) || ((iiu + uFloor) >= this.PATTERN_GRID[0].length) || (this.PATTERN_GRID[iiv + vFloor][iiu + uFloor] == null) || (this.PATTERN_GRID[iiv + vFloor][iiu + uFloor][0] == null); if (this.debugLevel > 1) System.out.println((iiu + uFloor) + ":" + (iiv + vFloor) + " " + (iinValid ? "---" : (IJ.d2s(this.PATTERN_GRID[iiv + vFloor][iiu + uFloor][0][0], 1) + ":" + IJ.d2s(this.PATTERN_GRID[iiv + vFloor][iiu + uFloor][0][1], 1)))); } } return null; // pointer too far from the grid (outside of the grid) } return result; } /* ======================================================================== */ /* * public static MatchSimulatedPattern.LaserPointer LASER_POINTERS= new * MatchSimulatedPattern.LaserPointer ( * */ // /* ======================================================================== */ private double[] correctedPatternCrossLocation( LwirReaderParameters lwirReaderParameters, // null is OK double[] beforeXY, // initial coordinates of the pattern cross point double wv0x, double wv0y, double wv1x, double wv1y, double[][] correction, ImagePlus imp, // image data (Bayer mosaic) DistortionParameters distortionParameters, // MatchSimulatedPattern.PatternDetectParameters patternDetectParameters, MatchSimulatedPattern matchSimulatedPattern, // correlationSize SimulationPattern.SimulParameters thisSimulParameters, boolean equalizeGreens, double[] window, // window function double[] window2, // window function - twice FFT size (or null) double[] window4, // window function - 4x FFT size (or null) SimulationPattern simulationPattern, boolean negative, // invert cross phase DoubleFHT fht_instance, boolean fast, // use fast measuring of the maximum on the correlation double[][] locsNeib, // locations and weights of neighbors to average int debug_level, String dbgStr) { if (distortionParameters.legacyMode) return correctedPatternCrossLocationOld( beforeXY, // initial coordinates of the pattern cross point wv0x, wv0y, wv1x, wv1y, correction, imp, // image data (Bayer mosaic) distortionParameters, // patternDetectParameters, matchSimulatedPattern, // correlationSize thisSimulParameters, equalizeGreens, window, // window function window2, // window function - twice FFT size (or null) window4, // window function - 4x FFT size (or null) simulationPattern, negative, // invert cross phase fht_instance, fast, // use fast measuring of the maximum on the correlation locsNeib, // locations and weights of neighbors to average debug_level); else return correctedPatternCrossLocationAverage4( lwirReaderParameters, // LwirReaderParameters beforeXY, // initial coordinates of the pattern cross point wv0x, wv0y, wv1x, wv1y, correction, imp, // image data (Bayer mosaic) distortionParameters, // patternDetectParameters, matchSimulatedPattern, // correlationSize thisSimulParameters, equalizeGreens, window, // window function window2, // window function - twice FFT size (or null) window4, // window function - 4x FFT size (or null) simulationPattern, negative, // invert cross phase fht_instance, fast, // use fast measuring of the maximum on the correlation locsNeib, // locations and weights of neighbors to average debug_level, dbgStr); } private double[] correctedPatternCrossLocationOld(double[] beforeXY, // initial coordinates of the pattern cross // point double wv0x, double wv0y, double wv1x, double wv1y, double[][] correction, ImagePlus imp, // image data // (Bayer // mosaic) DistortionParameters distortionParameters, // MatchSimulatedPattern.PatternDetectParameters patternDetectParameters, MatchSimulatedPattern matchSimulatedPattern, // correlationSize SimulationPattern.SimulParameters thisSimulParameters, boolean equalizeGreens, double[] window, // window // function double[] window2, // window function - twice FFT size (or null) double[] window4, // window function - 4x FFT size (or null) SimulationPattern simulationPattern, boolean negative, // invert cross phase DoubleFHT fht_instance, boolean fast, // use fast measuring of the maximum on the correlation double[][] locsNeib, // locations and weights of neighbors to average int debug_level) { // Just for testing beforeXY[0] += distortionParameters.correlationDx; // offset, X (in pixels) beforeXY[1] += distortionParameters.correlationDy; // offset y (in pixels) double[][] convMatrix = { { 1.0, -1.0 }, { 1.0, 1.0 } }; // from greens2 to pixel WV double[][] invConvMatrix = matrix2x2_scale(matrix2x2_invert(convMatrix), 2.0); double[] result = new double[3]; result[0] = beforeXY[0]; result[1] = beforeXY[1]; result[2] = 0.0; // contrast if (fht_instance == null) fht_instance = new DoubleFHT(); // move upstream to reduce number of initializations // create diagonal green selection around ixc,iyc double[][] wv = { { wv0x, wv0y }, { wv1x, wv1y } }; double[][] WVgreens = matrix2x2_mul(wv, invConvMatrix); if (debug_level > 2) System.out.println("WVgreens[0][0]=" + IJ.d2s(WVgreens[0][0], 3) + " WVgreens[0][1]=" + IJ.d2s(WVgreens[0][1], 3) + " WVgreens[1][0]=" + IJ.d2s(WVgreens[1][0], 3) + " WVgreens[1][1]=" + IJ.d2s(WVgreens[1][1], 3)); double[] dUV; double[][] sim_pix; double[] simGreensCentered; double[] modelCorr; double[] xyCorr = { 0.0, 0.0 }; double[] centerXY; double contrast; int numNeib; double[] corr = null; double[] neibCenter = new double[2]; if (correction != null) { // overwrite wave vectors wv[0][0] = correction[0][0]; wv[0][1] = correction[0][1]; wv[1][0] = correction[1][0]; wv[1][1] = correction[1][1]; if (correction[0].length > 3) { // enough data for quadratic approximation corr = new double[10]; corr[0] = correction[0][3] / 4; corr[1] = correction[0][4] / 4; corr[2] = correction[0][5] / 4; corr[3] = correction[1][3] / 4; corr[4] = correction[1][4] / 4; corr[5] = correction[1][5] / 4; corr[6] = 0.0; corr[7] = 0.0; corr[9] = 0.0; corr[9] = 0.0; } } double u_span = Math.sqrt(wv0x * wv0x + wv0y * wv0y) * distortionParameters.correlationSize; double v_span = Math.sqrt(wv1x * wv1x + wv1y * wv1y) * distortionParameters.correlationSize; double min_span = Math.min(u_span, v_span); int thisCorrelationSize = distortionParameters.correlationSize; double[] thisWindow = window; double uv_threshold = distortionParameters.minUVSpan * 0.25 * Math.sqrt(2.0); if ((min_span < uv_threshold) && (window2 != null) && (thisCorrelationSize < distortionParameters.maximalCorrelationSize)) { // trying to increase only // twice thisCorrelationSize *= 2; min_span *= 2; thisWindow = window2; if ((min_span < uv_threshold) && (window4 != null) && (thisCorrelationSize < distortionParameters.maximalCorrelationSize)) { thisCorrelationSize *= 2; min_span *= 2; thisWindow = window4; } } setCorrelationSizesUsed(thisCorrelationSize); // if (thisCorrelationSize>distortionParameters.correlationSize) // System.out.println("**** u/v span too small, increasing FFT size to // "+thisCorrelationSize); if ((debug_level > 0) && (thisCorrelationSize > distortionParameters.correlationSize)) System.out.println("**** u/v span too small, increasing FFT size to " + thisCorrelationSize); Rectangle centerCross = correlationSelection(beforeXY, // initial coordinates of the pattern cross point // distortionParameters.correlationSize); thisCorrelationSize); int ixc = centerCross.x + centerCross.width / 2; int iyc = centerCross.y + centerCross.height / 2; double[] diffBeforeXY = { beforeXY[0] - ixc, beforeXY[1] - iyc }; double[][] input_bayer = splitBayer(imp, centerCross, equalizeGreens); if (debug_level > 3) ShowDoubleFloatArrays.showArrays(input_bayer, true, "centered"); if (debug_level > 2) ShowDoubleFloatArrays.showArrays(input_bayer[4], "greens"); if (debug_level > 2) System.out.println("ixc=" + ixc + " iyc=" + iyc); double[] greens = normalizeAndWindow(input_bayer[4], thisWindow); if (debug_level > 2) { System.out.println(" wv0x=" + IJ.d2s(wv0x, 5) + " wv0y=" + IJ.d2s(wv0y, 5)); System.out.println(" wv1x=" + IJ.d2s(wv1x, 5) + " wv1y=" + IJ.d2s(wv1y, 5)); System.out.println(" u-span=" + IJ.d2s(u_span, 3) + " v-span=" + IJ.d2s(v_span, 3) + " threshold=" + IJ.d2s(uv_threshold, 3) + " (" + IJ.d2s(distortionParameters.minUVSpan, 3) + ")"); if (corr != null) { System.out.println(" Ax=" + IJ.d2s(corr[0], 8) + " Bx=" + IJ.d2s(corr[1], 8) + " Cx=" + IJ.d2s(corr[2], 8) + " Dx=" + IJ.d2s(corr[6], 8) + " Ex=" + IJ.d2s(corr[7], 8)); System.out.println(" Ay=" + IJ.d2s(corr[3], 8) + " By=" + IJ.d2s(corr[4], 8) + " Cy=" + IJ.d2s(corr[5], 8) + " Dy=" + IJ.d2s(corr[8], 8) + " Ey=" + IJ.d2s(corr[9], 8)); } } for (numNeib = 0; numNeib < locsNeib.length; numNeib++) if (locsNeib[numNeib][2] != 0.0) { neibCenter[0] = diffBeforeXY[0] + locsNeib[numNeib][0]; neibCenter[1] = diffBeforeXY[1] + locsNeib[numNeib][1]; // dUV=matrix2x2_scale(matrix2x2_mul(wv,diffBeforeXY),-2*Math.PI); dUV = matrix2x2_scale(matrix2x2_mul(wv, neibCenter), -2 * Math.PI); simulationPattern.simulatePatternFullPattern( // not thread safe wv0x, wv0y, dUV[0] + (negative ? (-Math.PI / 2) : Math.PI / 2), // negative?(-Math.PI/2):Math.PI/2, wv1x, wv1y, dUV[1] + Math.PI / 2, // Math.PI/2, corr, // null, // no mesh distortion here thisSimulParameters.subdiv, // SIMUL.subdiv, - do not need high quality here thisCorrelationSize, true, // center for greens false);// boolean mono sim_pix = simulationPattern.extractSimulPatterns(thisSimulParameters, 1, // subdivide output pixels thisCorrelationSize, // number of Bayer cells in width of the square selection (half number of // pixels) 0, 0); if ((debug_level > 2) && (numNeib == 0)) { // if (debug_level>2){ System.out.println("==========Showing simul" + ixc + ":" + iyc); ShowDoubleFloatArrays.showArrays(sim_pix[4].clone(), "simul" + ixc + ":" + iyc); } simGreensCentered = normalizeAndWindow(sim_pix[4], thisWindow); // if ((debug_level>2) && (numNeib==0)){ if (debug_level > 2) { System.out.println("==========Showing simGreensCentered" + ixc + ":" + iyc); ShowDoubleFloatArrays.showArrays(simGreensCentered.clone(), "simGreensCentered" + ixc + ":" + iyc); ShowDoubleFloatArrays.showArrays(greens.clone(), "greensWidowed" + ixc + ":" + iyc); // System.out.println("debug_level="+debug_level+" *** Remove next line ***"); // sim_pix[14]=null; // make it crash here } modelCorr = fht_instance.correlate(greens.clone(), // measured pixel array // modelCorr=fht_instance.correlate (greens, // measured pixel array simGreensCentered, // simulated (model) pixel array) // distortionParameters.correlationHighPassSigma); distortionParameters.correlationHighPassSigma, distortionParameters.correlationLowPassSigma, distortionParameters.phaseCorrelationFraction); // if ((debug_level>2) && (numNeib==0)){ if (debug_level > 2) { System.out.println("==========Showing modelCorr" + ixc + ":" + iyc); ShowDoubleFloatArrays.showArrays(modelCorr, "modelCorr" + ixc + ":" + iyc); } // xyCorr=new double[2]; //???????????????????? // Use fast, but less precise method here ? // if (numNeib==0) System.out.println ("correctedPatternCrossLocation(): // debugLevel="+debugLevel+" fast="+fast); if (fast) centerXY = correlationMaximum(modelCorr, distortionParameters.correlationMaxOffset, (debug_level > 2) && (numNeib == 0)); else centerXY = correlationMaximum(modelCorr, distortionParameters.correlationRadius, distortionParameters.correlationThreshold, // double threshold, // fraction of maximum // (slightly less than 1.0) to limit the top // part of the maximum for centroid distortionParameters.correlationSubdiv, distortionParameters.correlationFFTSubdiv, fht_instance, distortionParameters.correlationMaxOffset, 0.0, // lowpass filtering already // done (debug_level > 2) && ((numNeib == 0) || (passNumber > 1))); if (centerXY == null) { if (debug_level > 0) System.out.println("Too far from the center0 (" + beforeXY[0] + "/" + beforeXY[1] + ")"); return null; } // Verify contrast (if specified) - only for the center sample (numNeib==0) if (numNeib == 0) { double[] contrasts = correlationContrast(modelCorr, greens, WVgreens, // wave vectors (same units as // the pixels array) // distortionParameters.correlationRingWidth, // ring (around r=0.5 dist to // opposite corr) width distortionParameters.contrastSelectSigmaCenter, // Gaussian sigma to select correlation // centers (pixels, 2.0) distortionParameters.contrastSelectSigma, // Gaussian sigma to select correlation centers // (fraction of UV period), 0.1 // TODO: verify that displacement is correct here (sign, direction) centerXY[0], // x0, // center coordinates centerXY[1], // y0, "test-contrast"); // title base for optional plots names contrast = contrasts[0]; result[2] = contrast; if (Double.isNaN(contrasts[0]) || ((distortionParameters.correlationMinContrast > 0) && (contrasts[0] < distortionParameters.correlationMinContrast))) { if (debug_level > 1) System.out.println("Center contrast too low - " + contrasts[0] + "<" + distortionParameters.correlationMinContrast); if (debug_level > 1) System.out.println("Center contrast " + IJ.d2s(contrasts[0], 3) + " (" + distortionParameters.correlationMinContrast + ")" + " is too low (" + IJ.d2s(beforeXY[0], 3) + "/" + IJ.d2s(beforeXY[1], 3) + ")->" + IJ.d2s(centerXY[0], 3) + "/" + IJ.d2s(centerXY[1], 3)); return null; } else { if (debug_level > 1) System.out.println("Contrast " + IJ.d2s(contrasts[0], 3) + " (" + distortionParameters.correlationMinContrast + ")" + " is good (" + IJ.d2s(beforeXY[0], 3) + "/" + IJ.d2s(beforeXY[1], 3) + ")->" + IJ.d2s(centerXY[0], 3) + "/" + IJ.d2s(centerXY[1], 3)); } if (Double.isNaN(contrasts[1]) || ((distortionParameters.correlationMinAbsoluteContrast > 0) && (contrasts[1] < distortionParameters.correlationMinAbsoluteContrast))) { if (debug_level > 1) System.out.println("Absolute contrast too low - " + contrasts[1] + "<" + distortionParameters.correlationMinAbsoluteContrast); if (debug_level > 1) System.out.println("Absolute contrast " + IJ.d2s(contrasts[1], 3) + " (" + distortionParameters.correlationMinAbsoluteContrast + ")" + " is too low (" + IJ.d2s(beforeXY[0], 3) + "/" + IJ.d2s(beforeXY[1], 3) + ")->" + IJ.d2s(centerXY[0], 3) + "/" + IJ.d2s(centerXY[1], 3)); return null; } if (debug_level > 2) System.out.println("Contarst=" + contrast + " (legacy)"); } if (debug_level > 2) System.out.println("correctedPatternCrossLocation: Center x=" + IJ.d2s(centerXY[0], 3) + " y=" + IJ.d2s(centerXY[1], 3)); // convert from diagonal greens coordinates to sensor pixel coordinates xyCorr[0] += (-centerXY[0] - centerXY[1]) * locsNeib[numNeib][2]; xyCorr[1] += (centerXY[0] - centerXY[1]) * locsNeib[numNeib][2];/* * if (debug_array!=null) { * debug_array[9][0]+=(-centerXY[0]-centerXY[1])*locsNeib[numNeib][2]; * debug_array[9][1]+=( centerXY[0]-centerXY[1])*locsNeib[numNeib][2]; } */ if (debug_level > 1) System.out.println("correctedPatternCrossLocation: dist=" + IJ.d2s(Math.sqrt(xyCorr[0] * xyCorr[0] + xyCorr[1] * xyCorr[1]), 4) + " xyCorr[0]=" + IJ.d2s(xyCorr[0], 4) + " xyCorr[1]=" + IJ.d2s(xyCorr[1], 4)); } // average xyCorr[] // result[0]=ixc-xyCorr[0]; // result[1]=iyc-xyCorr[1]; // disabling correction !!!!!!!!!!!!!!!!!!!!!!! result[0] = ixc - xyCorr[0] + diffBeforeXY[0]; result[1] = iyc - xyCorr[1] + diffBeforeXY[1]; // result[0]=ixc+diffBeforeXY[0]; // result[1]=iyc+diffBeforeXY[1]; if (debug_level > 2) System.out.println("---correctedPatternCrossLocation: before x=" + IJ.d2s(beforeXY[0], 3) + " y=" + IJ.d2s(beforeXY[1], 3)); if (debug_level > 2) System.out.println("+++correctedPatternCrossLocation: after x=" + IJ.d2s(result[0], 3) + " y=" + IJ.d2s(result[1], 3)); // if (debug_level>0) System.out.println("---correctedPatternCrossLocation: // before x="+IJ.d2s(beforeXY[0],3)+" y="+IJ.d2s(beforeXY[1],3)); // if (debug_level>0) System.out.println("+++correctedPatternCrossLocation: // after x="+IJ.d2s(result[0],3)+" y="+IJ.d2s(result[1],3)); return result; } private double[] correctedPatternCrossLocationAverage4( LwirReaderParameters lwirReaderParameters, // null is OK double[] beforeXY, // initial coordinates of the pattern cross point double wv0x, double wv0y, double wv1x, double wv1y, double[][] correction, ImagePlus imp, // image data (Bayer mosaic) DistortionParameters distortionParameters, // distortionParameters.refineCorrelations MatchSimulatedPattern.PatternDetectParameters patternDetectParameters, MatchSimulatedPattern matchSimulatedPattern, // correlationSize SimulationPattern.SimulParameters thisSimulParameters, boolean equalizeGreens, double[] window, // window function double[] window2, // window function - twice FFT size (or null) double[] window4, // window function - 4x FFT size (or null) SimulationPattern simulationPattern, boolean negative, // invert cross phase DoubleFHT fht_instance, boolean fast, // use fast measuring of the maximum on the correlation double[][] locsNeib, // locations and weights of neighbors to average int debug_level, String dbgStr) { if (imp == null) { return null; } /* boolean is_lwir = ((lwirReaderParameters != null) && lwirReaderParameters.is_LWIR(imp)); int correlation_size = is_lwir ? distortionParameters.correlationSizeLwir : distortionParameters.correlationSize; int max_correlation_size = is_lwir ? distortionParameters.maximalCorrelationSizeLwir : distortionParameters.maximalCorrelationSize; */ final int sensor_type = LwirReaderParameters.sensorType(imp); final int correlation_size = distortionParameters.getCorrelationSize(sensor_type); final int max_correlation_size = distortionParameters.getMaximalCorrelationSize(sensor_type); boolean is_mono = false; try { is_mono = Boolean.parseBoolean((String) imp.getProperty("MONOCHROME")); } catch (Exception e) { } is_mono |= (sensor_type == 1); // is_lwir; int debug_threshold = 3; // next print - same for good and bad, correction==null if (dbgStr != null) System.out.println(dbgStr + ": wv0x=" + wv0x + " wv0y=" + wv0y + " wv1x=" + wv1x + " wv1y=" + wv1y + " beforeXY[0]=" + beforeXY[0] + ", beforeXY[1]=" + beforeXY[1] + " correction is " + ((correction == null) ? "null" : "not null")); boolean dbgThis = (Math.abs(beforeXY[0] - patternDetectParameters.debugX) < patternDetectParameters.debugRadius) && (Math.abs(beforeXY[1] - patternDetectParameters.debugY) < patternDetectParameters.debugRadius); // dbgThis=true; // dbgThis=true; if (dbgThis) { System.out.println("correctedPatternCrossLocationAverage4(), beforeXY[0]=" + beforeXY[0] + ", beforeXY[1]=" + beforeXY[1]); debug_level += 3; } // Just for testing beforeXY[0] += distortionParameters.correlationDx; // offset, X (in pixels) beforeXY[1] += distortionParameters.correlationDy; // offset y (in pixels) double[][] invConvMatrix = { { 1, 0 }, { 0, 1 } }; // identity if (sensor_type != 1) { //(!is_lwir) { double[][] convMatrix = { { 1.0, -1.0 }, { 1.0, 1.0 } }; // from greens2 to pixel WV invConvMatrix = matrix2x2_scale(matrix2x2_invert(convMatrix), 2.0); } double[] result = new double[3]; result[0] = beforeXY[0]; result[1] = beforeXY[1]; result[2] = 0.0; // contrast if (fht_instance == null) fht_instance = new DoubleFHT(); // move upstream to reduce number of initializations // create diagonal green selection around ixc,iyc double[][] wv = { { wv0x, wv0y }, { wv1x, wv1y } }; double[][] WVgreensMono = matrix2x2_mul(wv, invConvMatrix); // rotated for greens, same dir for mono (lwir) if (debug_level > debug_threshold) System.out.println("WVgreensMono[0][0]=" + IJ.d2s(WVgreensMono[0][0], 3) + " WVgreensMono[0][1]=" + IJ.d2s(WVgreensMono[0][1], 3) + " WVgreensMono[1][0]=" + IJ.d2s(WVgreensMono[1][0], 3) + " WVgreensMono[1][1]=" + IJ.d2s(WVgreensMono[1][1], 3)); double[] dUV; // double [] simGreensCentered; double[] simCentered; // double [] modelCorr; double[] centerXY; double contrast; int numNeib; double[] corr = null; double[] neibCenter = new double[2]; if (correction != null) { // overwrite wave vectors wv[0][0] = correction[0][0]; wv[0][1] = correction[0][1]; wv[1][0] = correction[1][0]; wv[1][1] = correction[1][1]; if (correction[0].length > 3) { // enough data for quadratic approximation corr = new double[10]; corr[0] = correction[0][3] / 4; corr[1] = correction[0][4] / 4; corr[2] = correction[0][5] / 4; corr[3] = correction[1][3] / 4; corr[4] = correction[1][4] / 4; corr[5] = correction[1][5] / 4; corr[6] = 0.0; corr[7] = 0.0; corr[9] = 0.0; corr[9] = 0.0; } } double u_span = Math.sqrt(wv0x * wv0x + wv0y * wv0y) * correlation_size; double v_span = Math.sqrt(wv1x * wv1x + wv1y * wv1y) * correlation_size; double min_span = Math.min(u_span, v_span); int thisCorrelationSize = correlation_size; double[] thisWindow = window; double uv_threshold = distortionParameters.minUVSpan * 0.25 * Math.sqrt(2.0); if ((min_span < uv_threshold) && (window2 != null) && (thisCorrelationSize < max_correlation_size)) { // trying // to // increase // only // twice thisCorrelationSize *= 2; min_span *= 2; thisWindow = window2; if ((min_span < uv_threshold) && (window4 != null) && (thisCorrelationSize < max_correlation_size)) { thisCorrelationSize *= 2; min_span *= 2; thisWindow = window4; } } setCorrelationSizesUsed(thisCorrelationSize); if ((debug_level > (debug_threshold - 2)) && (thisCorrelationSize > correlation_size)) System.out.println("**** u/v span too small, increasing FFT size to " + thisCorrelationSize); Rectangle centerCross = correlationSelection(beforeXY, // initial coordinates of the pattern cross point // (is_lwir ? (thisCorrelationSize / 2) : (thisCorrelationSize))); ((sensor_type == 1) ? (thisCorrelationSize / 2) : (thisCorrelationSize))); int ixc = centerCross.x + centerCross.width / 2; int iyc = centerCross.y + centerCross.height / 2; double[] diffBeforeXY = { beforeXY[0] - ixc, beforeXY[1] - iyc }; double[] greens_mono; // greens or mono // if (is_lwir) { if (sensor_type == 1) { greens_mono = getNoBayer(imp, centerCross); if (debug_level > (debug_threshold + 0)) ShowDoubleFloatArrays.showArrays(greens_mono, "greens_mono"); if (debug_level > (debug_threshold + 0)) System.out.println("ixc=" + ixc + " iyc=" + iyc); normalizeAndWindow(greens_mono, thisWindow); // Twice lower contrast than EO - doubling below - did not work // for (int i = 0; i < greens_mono.length; i++) { // greens_mono[i] *= 2.0; // } } else { double[][] input_bayer = splitBayer(imp, centerCross, equalizeGreens); if (debug_level > (debug_threshold + 1)) ShowDoubleFloatArrays.showArrays(input_bayer, true, "centered"); if (debug_level > (debug_threshold + 0)) ShowDoubleFloatArrays.showArrays(input_bayer[4], "greens"); if (debug_level > (debug_threshold + 0)) System.out.println("ixc=" + ixc + " iyc=" + iyc); greens_mono = normalizeAndWindow(input_bayer[4], thisWindow); } if (debug_level > (debug_threshold + 0)) ShowDoubleFloatArrays.showArrays(greens_mono, "greens_mono_Windowed"); // average is not zero - probably if (debug_level > (debug_threshold + 0)) { System.out.println(" wv0x=" + IJ.d2s(wv0x, 5) + " wv0y=" + IJ.d2s(wv0y, 5)); System.out.println(" wv1x=" + IJ.d2s(wv1x, 5) + " wv1y=" + IJ.d2s(wv1y, 5)); System.out.println(" u-span=" + IJ.d2s(u_span, 3) + " v-span=" + IJ.d2s(v_span, 3) + " threshold=" + IJ.d2s(uv_threshold, 3) + " (" + IJ.d2s(distortionParameters.minUVSpan, 3) + ")"); if (corr != null) { System.out.println(" Ax=" + IJ.d2s(corr[0], 8) + " Bx=" + IJ.d2s(corr[1], 8) + " Cx=" + IJ.d2s(corr[2], 8) + " Dx=" + IJ.d2s(corr[6], 8) + " Ex=" + IJ.d2s(corr[7], 8)); System.out.println(" Ay=" + IJ.d2s(corr[3], 8) + " By=" + IJ.d2s(corr[4], 8) + " Cy=" + IJ.d2s(corr[5], 8) + " Dy=" + IJ.d2s(corr[8], 8) + " Ey=" + IJ.d2s(corr[9], 8)); } } int[][] gridNeib = { { 0, 0 }, { 0, 1 }, { 1, 0 }, { 1, 1 } }; int numOfNeib = distortionParameters.correlationAverageOnRefine ? gridNeib.length : 1; if (debug_level > (debug_threshold + 0)) { System.out.println(" numOfNeib=" + numOfNeib + " (distortionParameters.correlationAverageOnRefine=" + distortionParameters.correlationAverageOnRefine); } if (locsNeib.length == 1) { numOfNeib = 1; // on the first pass, from legacy if (debug_level > (debug_threshold + 1)) { System.out.println("Reduced numOfNeib to " + numOfNeib + " as locsNeib.length=" + locsNeib.length); } } if (dbgStr != null) { double dbgSumWindow = 0.0; for (double dbgD : thisWindow) dbgSumWindow += dbgD; // All he same - good/bad System.out.println(dbgStr + ": thisCorrelationSize=" + thisCorrelationSize + " min_span=" + min_span + " dbgSumWindow=" + dbgSumWindow + "locsNeib.length=" + locsNeib.length + " fast=" + fast + " numOfNeib=" + numOfNeib + " (distortionParameters.correlationAverageOnRefine=" + distortionParameters.correlationAverageOnRefine); } double[][] modelCorrs = new double[numOfNeib][]; double[][] debugGreens = new double[numOfNeib][0]; for (numNeib = 0; numNeib < numOfNeib; numNeib++) { // if (is_lwir) { // monochrome, use all pixels if (sensor_type == 1) { // monochrome, use all pixels neibCenter[0] = diffBeforeXY[0] + gridNeib[numNeib][0]; neibCenter[1] = diffBeforeXY[1] + gridNeib[numNeib][1]; } else { neibCenter[0] = diffBeforeXY[0] + 0.5 * (gridNeib[numNeib][0] + gridNeib[numNeib][1]); neibCenter[1] = diffBeforeXY[1] + 0.5 * (gridNeib[numNeib][0] - gridNeib[numNeib][1]); } double[] barray; // if (is_lwir) { if (sensor_type == 1) { // negative=!negative; dUV = matrix2x2_scale(matrix2x2_mul(wv, neibCenter), -2 * Math.PI); // dUV[0] = 0.0; dUV[1] = 0.0; boolean dbg_once = false; if (dbg_once || (debug_level > (debug_threshold + 20))) { double[] barray0 = simulationPattern.simulatePatternFullPatternSafe( // Is it the most // time-consuming part? // should it be done once // and then only extraction // separate? wv0x, wv0y, (negative ? (-Math.PI / 2) : Math.PI / 2), // negative?(-Math.PI/2):Math.PI/2, wv1x, wv1y, Math.PI / 2, // Math.PI/2, corr, // null, // no mesh distortion here thisSimulParameters.subdiv, // SIMUL.subdiv, - do not need high quality here thisCorrelationSize, false, // false); // center for greens ??? false);// boolean mono double[] barray1 = simulationPattern.simulatePatternFullPatternSafe( // Is it the most // time-consuming part? // should it be done once // and then only extraction // separate? wv0x, wv0y, (negative ? (-Math.PI / 2) : Math.PI / 2), // negative?(-Math.PI/2):Math.PI/2, wv1x, wv1y, Math.PI / 2, // Math.PI/2, corr, // null, // no mesh distortion here thisSimulParameters.subdiv, // SIMUL.subdiv, - do not need high quality here thisCorrelationSize, true, // false); // center for greens ??? false);// boolean mono double[] barray2 = simulationPattern.simulatePatternFullPatternSafe( // Is it the most // time-consuming part? // should it be done once // and then only extraction // separate? wv0x, wv0y, (negative ? (-Math.PI / 2) : Math.PI / 2), // negative?(-Math.PI/2):Math.PI/2, wv1x, wv1y, Math.PI / 2, // Math.PI/2, corr, // null, // no mesh distortion here thisSimulParameters.subdiv, // SIMUL.subdiv, - do not need high quality here thisCorrelationSize, true, // false); // center for greens ??? true);// boolean mono double[][] dbg_barray = { barray0, barray1, barray2 }; System.out.println(">=========Showing barray01" + ixc + ":" + iyc); ShowDoubleFloatArrays.showArrays(dbg_barray, true, "barray" + ixc + ":" + iyc); double[] sim_pix0 = simulationPattern.extractSimulMono(barray0, thisSimulParameters, 1, // subdivide // output // pixels thisCorrelationSize, // number of Bayer cells in width of the square selection (half number // of pixels) 0, 0); double[] sim_pix1 = simulationPattern.extractSimulMono(barray1, thisSimulParameters, 1, // subdivide // output // pixels thisCorrelationSize, // number of Bayer cells in width of the square selection (half number // of pixels) 0, 0); double[] sim_pix2 = simulationPattern.extractSimulMono(barray2, thisSimulParameters, 1, // subdivide // output // pixels thisCorrelationSize, // number of Bayer cells in width of the square selection (half number // of pixels) 0, 0); double[][] dbg_sim_pix = { sim_pix0, sim_pix1, sim_pix2 }; System.out.println(">=========Showing barray01" + ixc + ":" + iyc); ShowDoubleFloatArrays.showArrays(dbg_sim_pix, true, "sim_pix" + ixc + ":" + iyc); } barray = simulationPattern.simulatePatternFullPatternSafe( // Is it the most time-consuming part? should // it be done once and then only extraction // separate? wv0x, wv0y, dUV[0] + (negative ? (-Math.PI / 2) : Math.PI / 2), // negative?(-Math.PI/2):Math.PI/2, wv1x, wv1y, dUV[1] + Math.PI / 2, // Math.PI/2, corr, // null, // no mesh distortion here thisSimulParameters.subdiv, // SIMUL.subdiv, - do not need high quality here thisCorrelationSize, true, // false); // center for greens ??? true);// boolean mono if (debug_level > (debug_threshold + 0)) { System.out.println(">=========Showing barray" + ixc + ":" + iyc); ShowDoubleFloatArrays.showArrays(barray, "barray" + ixc + ":" + iyc); } // barray for dUV=={0,0} is symmetrical around center pixel, // sim_pix - around {center - 0.5, center - 0.5} // for center_for_g2 - sim_pix is symmetrical around [center,center], // for !mono && !center_for_g2 - [center+0.5, center+0.5] // TODO: reduce size of barray for mono twice in each direction double[] sim_pix = simulationPattern.extractSimulMono(barray, thisSimulParameters, 1, // subdivide // output pixels thisCorrelationSize, // number of Bayer cells in width of the square selection (half number of // pixels) 0, 0); if (sim_pix == null) { System.out.println("***** BUG: extractSimulPatterns() FAILED *****"); return null; } if (dbgStr != null) { double dbgSumWindow = 0.0; for (double dbgD : sim_pix) dbgSumWindow += dbgD; System.out.println(dbgStr + ": SUM of sim_pix=" + dbgSumWindow); // First difference good/bad } simCentered = normalizeAndWindow(sim_pix, thisWindow); } else { dUV = matrix2x2_scale(matrix2x2_mul(wv, neibCenter), -2 * Math.PI); barray = simulationPattern.simulatePatternFullPatternSafe( // Is it the most time-consuming part? should // it be done once and then only extraction // separate? wv0x, wv0y, dUV[0] + (negative ? (-Math.PI / 2) : Math.PI / 2), // negative?(-Math.PI/2):Math.PI/2, wv1x, wv1y, dUV[1] + Math.PI / 2, // Math.PI/2, corr, // null, // no mesh distortion here thisSimulParameters.subdiv, // SIMUL.subdiv, - do not need high quality here thisCorrelationSize, true, // center for greens false);// boolean mono if (debug_level > (debug_threshold + 0)) { System.out.println(">=========Showing barray" + ixc + ":" + iyc); ShowDoubleFloatArrays.showArrays(barray, "barray" + ixc + ":" + iyc); } // double[][] sim_pix; double[][] sim_pix = null; if (is_mono) { sim_pix = new double[1][]; sim_pix[0] = simulationPattern.extractSimulMono( // TODO: can use twice smaller barray barray, thisSimulParameters, 1, // subdivide output pixels - now 4 thisCorrelationSize, // number of Bayer cells in width of the square selection (half number // of pixels) 0, // selection center, X (in pixels) 0); } else { sim_pix = simulationPattern.extractSimulPatterns(barray, thisSimulParameters, 1, // subdivide output // pixels thisCorrelationSize, // number of Bayer cells in width of the square selection (half number // of pixels) 0, 0); } if (sim_pix == null) { System.out.println("***** BUG: extractSimulPatterns() FAILED *****"); return null; } if (dbgStr != null) { double dbgSumWindow = 0.0; for (double[] dbgSlice : sim_pix) for (double dbgD : dbgSlice) dbgSumWindow += dbgD; System.out.println(dbgStr + ": SUM of sim_pix=" + dbgSumWindow); // First difference good/bad } simCentered = normalizeAndWindow(sim_pix[4], thisWindow); } if (dbgStr != null) { double dbgSumWindow = 0.0; for (double dbgD : simCentered) dbgSumWindow += dbgD; System.out.println(dbgStr + ": SUM of simGreensCentered=" + dbgSumWindow); } debugGreens[numNeib] = simCentered.clone(); modelCorrs[numNeib] = fht_instance.phaseCorrelate(greens_mono.clone(), simCentered, patternDetectParameters.phaseCoeff, 0, // 0.5, distortionParameters.correlationHighPassSigma, patternDetectParameters.lowpass_sigma, // 0.3, (fast?distortionParameters.correlationLowPassSigma:0.0),// // moved to decimation via FFT null, null); if (dbgStr != null) { double dbgSumWindow = 0.0; for (double[] dbgSlice : modelCorrs) for (double dbgD : dbgSlice) dbgSumWindow += dbgD; System.out.println(dbgStr + ": SUM of modelCorrs=" + dbgSumWindow); } } if (debug_level > (debug_threshold + 0)) { System.out.println(">=========Showing simCentered" + ixc + ":" + iyc); ShowDoubleFloatArrays.showArrays(debugGreens, true, "simCentered" + ixc + ":" + iyc); } if (debug_level > (debug_threshold + 0)) { System.out.println(">=========Showing modelCorrs, passNumber=" + passNumber); ShowDoubleFloatArrays.showArrays(modelCorrs, true, "modelCorrs:" + numOfNeib); } // combine 4 correlations into the double resolution, same output size (so half // input size) array int halfSize = thisCorrelationSize / 2; int qSize = thisCorrelationSize / 4; int thisFFTSubdiv = distortionParameters.correlationFFTSubdiv; double thisLowpass = distortionParameters.correlationLowPassSigma; double[] modelCorr; if (numOfNeib > 1) { modelCorr = new double[thisCorrelationSize * thisCorrelationSize]; for (int i = 0; i < modelCorr.length; i++) modelCorr[i] = 0.0; for (int dy = 0; dy < 2; dy++) for (int dx = 0; dx < 2; dx++) { for (int y = 0; y < halfSize; y++) for (int x = 0; x < halfSize; x++) { modelCorr[(2 * y + dy) * thisCorrelationSize + (2 * x + dx)] += modelCorrs[2 * dy + dx][(qSize + y) * thisCorrelationSize + (qSize + x)]; } } thisLowpass /= 2.0; // the lower the value, the more filtering. Decimated twice,so low pass // filtering - accordingly thisFFTSubdiv = (thisFFTSubdiv > 1) ? (thisFFTSubdiv / 2) : 1; } else { modelCorr = modelCorrs[0]; // also - different size } if (debug_level > (debug_threshold + 0)) { System.out.println(">==========Showing modelCorr"); ShowDoubleFloatArrays.showArrays(modelCorr, thisCorrelationSize, thisCorrelationSize, "modelCorr"); } if (fast) centerXY = correlationMaximum( // maybe twice actual size if modelCorr, distortionParameters.correlationMaxOffset, (debug_level > (debug_threshold + 0)) && (numNeib == 0)); // low-pass filtering should already be // done else centerXY = correlationMaximum(modelCorr, distortionParameters.correlationRadius, distortionParameters.correlationThreshold, // double threshold, // fraction of maximum (slightly // less than 1.0) to limit the top part of the maximum // for centroid distortionParameters.correlationSubdiv, thisFFTSubdiv, fht_instance, distortionParameters.correlationMaxOffset, thisLowpass, // distortionParameters.correlationLowPassSigma // (debug_level>2) && (passNumber>1)); (debug_level > (debug_threshold + 0))); if (centerXY == null) { if (debug_level > (debug_threshold - 1)) System.out.println("Too far from the center01 (" + beforeXY[0] + "/" + beforeXY[1] + ")"); if (dbgStr != null) System.out.println(dbgStr + "- Too far from the center01 (" + beforeXY[0] + "/" + beforeXY[1] + ")"); return null; } if (numNeib > 1) { centerXY[0] *= 0.5; centerXY[1] *= 0.5; for (int i = 0; i < 2; i++) for (int j = 0; j < 2; j++) WVgreensMono[i][j] *= 0.5; } double[] contrasts = correlationContrast(modelCorr, greens_mono, WVgreensMono, // wave vectors (same units as the pixels array) distortionParameters.contrastSelectSigmaCenter, // 2.0 Gaussian sigma to select correlation (pixels, 2.0) distortionParameters.contrastSelectSigma, // 0.1 Gaussian sigma to select correlation centers (fraction of UV period), 0.1 centerXY[0], // x0, // center coordinates centerXY[1], // y0, "test-contrast"); // title base for optional plots names if ((debug_level > (debug_threshold - 1))) { System.out.println("contrast = " + contrasts[0]); } contrast = contrasts[0]; result[2] = contrast; // System.out.println("cobntrasts = "+contrasts[0]+", "+contrasts[1]); if (Double.isNaN(contrasts[0]) || ((distortionParameters.correlationMinContrast > 0) && (contrasts[0] < distortionParameters.correlationMinContrast))) { if ((debug_level > (debug_threshold - 1))) System.out.println( "Contrast too low - " + contrasts[0] + "<" + distortionParameters.correlationMinContrast); if (debug_level > (debug_threshold - 1)) System.out.println("Contrast " + IJ.d2s(contrasts[0], 3) + " (" + distortionParameters.correlationMinContrast + ")" + " is TOO LOW (" + IJ.d2s(beforeXY[0], 3) + "/" + IJ.d2s(beforeXY[1], 3) + ")->" + IJ.d2s(centerXY[0], 3) + "/" + IJ.d2s(centerXY[1], 3)); if (dbgStr != null) System.out.println(dbgStr + " - Contrast " + IJ.d2s(contrasts[0], 3) + " (" + distortionParameters.correlationMinContrast + ")" + " is TOO LOW (" + IJ.d2s(beforeXY[0], 3) + "/" + IJ.d2s(beforeXY[1], 3) + ")->" + IJ.d2s(centerXY[0], 3) + "/" + IJ.d2s(centerXY[1], 3)); return null; } else { if (debug_level > (debug_threshold - 1)) System.out.println("Contrast " + IJ.d2s(contrasts[0], 3) + " (" + distortionParameters.correlationMinContrast + ")" + " is GOOD (" + IJ.d2s(beforeXY[0], 3) + "/" + IJ.d2s(beforeXY[1], 3) + ")->" + IJ.d2s(centerXY[0], 3) + "/" + IJ.d2s(centerXY[1], 3)); if (dbgStr != null) System.out.println(dbgStr + " - Contrast " + IJ.d2s(contrasts[0], 3) + " (" + distortionParameters.correlationMinContrast + ")" + " is GOOD (" + IJ.d2s(beforeXY[0], 3) + "/" + IJ.d2s(beforeXY[1], 3) + ")->" + IJ.d2s(centerXY[0], 3) + "/" + IJ.d2s(centerXY[1], 3)); } if (Double.isNaN(contrasts[1]) || ((distortionParameters.correlationMinAbsoluteContrast > 0) && (contrasts[1] < distortionParameters.correlationMinAbsoluteContrast))) { if (debug_level > (debug_threshold - 1)) System.out.println("Absolute contrast too low - " + contrasts[1] + "<" + distortionParameters.correlationMinAbsoluteContrast); if (debug_level > (debug_threshold - 1)) System.out.println("Absolute contrast " + IJ.d2s(contrasts[1], 3) + " (" + distortionParameters.correlationMinAbsoluteContrast + ")" + " is too low (" + IJ.d2s(beforeXY[0], 3) + "/" + IJ.d2s(beforeXY[1], 3) + ")->" + IJ.d2s(centerXY[0], 3) + "/" + IJ.d2s(centerXY[1], 3)); if (dbgStr != null) System.out.println(dbgStr + " - Absolute contrast " + IJ.d2s(contrasts[1], 3) + " (" + distortionParameters.correlationMinAbsoluteContrast + ")" + " is too low (" + IJ.d2s(beforeXY[0], 3) + "/" + IJ.d2s(beforeXY[1], 3) + ")->" + IJ.d2s(centerXY[0], 3) + "/" + IJ.d2s(centerXY[1], 3)); return null; } else { if (dbgStr != null) System.out.println(dbgStr + " - Absolute contrast " + IJ.d2s(contrasts[1], 3) + " (" + distortionParameters.correlationMinAbsoluteContrast + ")" + " is GOOD (" + IJ.d2s(beforeXY[0], 3) + "/" + IJ.d2s(beforeXY[1], 3) + ")->" + IJ.d2s(centerXY[0], 3) + "/" + IJ.d2s(centerXY[1], 3)); } if (debug_level > (debug_threshold - 0)) System.out.println(">>>Contrast=" + contrasts[0] + "/" + contrasts[1] + " (" + IJ.d2s(beforeXY[0], 3) + ":" + IJ.d2s(beforeXY[1], 3) + ")->" + IJ.d2s(result[0], 3) + ":" + IJ.d2s(result[1], 3)); // FIXME: maybe wrong for mono? // if (is_lwir) { if (sensor_type == 1) { result[0] = ixc + diffBeforeXY[0] + centerXY[0]; result[1] = iyc + diffBeforeXY[1] + centerXY[1]; // Twice lower contrast than EO - doubling below - did not work // result[2] *= 2.0; // not just for refine } else { result[0] = ixc + diffBeforeXY[0] - (-centerXY[0] - centerXY[1]); result[1] = iyc + diffBeforeXY[1] - (centerXY[0] - centerXY[1]); } if (debug_level > (debug_threshold + 0)) System.out.println(">---correctedPatternCrossLocation: before x=" + IJ.d2s(beforeXY[0], 3) + " y=" + IJ.d2s(beforeXY[1], 3)); if (debug_level > (debug_threshold + 0)) System.out.println(">+++correctedPatternCrossLocation: after x=" + IJ.d2s(result[0], 3) + " y=" + IJ.d2s(result[1], 3)); return result; } // ======= end of private double [] correctedPatternCrossLocationAverage4() === private double[] correctedPatternCrossLocationAverage4TestOldNew(double[] beforeXY, // initial coordinates of the // pattern cross point double wv0x, double wv0y, double wv1x, double wv1y, double[][] correction, ImagePlus imp, // image data // (Bayer // mosaic) DistortionParameters distortionParameters, // distortionParameters.refineCorrelations MatchSimulatedPattern.PatternDetectParameters patternDetectParameters, MatchSimulatedPattern matchSimulatedPattern, // correlationSize SimulationPattern.SimulParameters thisSimulParameters, boolean equalizeGreens, double[] window, // window // function double[] window2, // window function - twice FFT size (or null) double[] window4, // window function - 4x FFT size (or null) SimulationPattern simulationPattern, boolean negative, // invert cross phase DoubleFHT fht_instance, boolean fast, // use fast measuring of the maximum on the correlation double[][] locsNeib, // locations and weights of neighbors to average int debug_level, String dbgStr) { int debug_threshold = 3; // next print - same for good and bad, correction==null if (dbgStr != null) System.out.println(dbgStr + ": wv0x=" + wv0x + " wv0y=" + wv0y + " wv1x=" + wv1x + " wv1y=" + wv1y + " beforeXY[0]=" + beforeXY[0] + ", beforeXY[1]=" + beforeXY[1] + " correction is " + ((correction == null) ? "null" : "not null")); boolean dbgThis = (Math.abs(beforeXY[0] - patternDetectParameters.debugX) < patternDetectParameters.debugRadius) && (Math.abs(beforeXY[1] - patternDetectParameters.debugY) < patternDetectParameters.debugRadius); dbgThis = true; if (dbgThis) { System.out.println("correctedPatternCrossLocationAverage4(), beforeXY[0]=" + beforeXY[0] + ", beforeXY[1]=" + beforeXY[1]); debug_level += 3; } // System.out.println("correctedPatternCrossLocationAverage4(): // beforeXY[0]="+beforeXY[0]+". beforeXY[1]="+beforeXY[1]); // Just for testing beforeXY[0] += distortionParameters.correlationDx; // offset, X (in pixels) beforeXY[1] += distortionParameters.correlationDy; // offset y (in pixels) double[][] convMatrix = { { 1.0, -1.0 }, { 1.0, 1.0 } }; // from greens2 to pixel WV double[][] invConvMatrix = matrix2x2_scale(matrix2x2_invert(convMatrix), 2.0); double[] result = new double[3]; result[0] = beforeXY[0]; result[1] = beforeXY[1]; result[2] = 0.0; // contrast if (fht_instance == null) fht_instance = new DoubleFHT(); // move upstream to reduce number of initializations // create diagonal green selection around ixc,iyc double[][] wv = { { wv0x, wv0y }, { wv1x, wv1y } }; double[][] WVgreens = matrix2x2_mul(wv, invConvMatrix); if (debug_level > debug_threshold) System.out.println("WVgreens[0][0]=" + IJ.d2s(WVgreens[0][0], 3) + " WVgreens[0][1]=" + IJ.d2s(WVgreens[0][1], 3) + " WVgreens[1][0]=" + IJ.d2s(WVgreens[1][0], 3) + " WVgreens[1][1]=" + IJ.d2s(WVgreens[1][1], 3)); double[] dUV; double[][] sim_pix; double[] simGreensCentered; // double [] modelCorr; double[] centerXY; double contrast; int numNeib; double[] corr = null; double[] neibCenter = new double[2]; if (correction != null) { // overwrite wave vectors wv[0][0] = correction[0][0]; wv[0][1] = correction[0][1]; wv[1][0] = correction[1][0]; wv[1][1] = correction[1][1]; if (correction[0].length > 3) { // enough data for quadratic approximation corr = new double[10]; corr[0] = correction[0][3] / 4; corr[1] = correction[0][4] / 4; corr[2] = correction[0][5] / 4; corr[3] = correction[1][3] / 4; corr[4] = correction[1][4] / 4; corr[5] = correction[1][5] / 4; corr[6] = 0.0; corr[7] = 0.0; corr[9] = 0.0; corr[9] = 0.0; } } double u_span = Math.sqrt(wv0x * wv0x + wv0y * wv0y) * distortionParameters.correlationSize; double v_span = Math.sqrt(wv1x * wv1x + wv1y * wv1y) * distortionParameters.correlationSize; double min_span = Math.min(u_span, v_span); int thisCorrelationSize = distortionParameters.correlationSize; double[] thisWindow = window; double uv_threshold = distortionParameters.minUVSpan * 0.25 * Math.sqrt(2.0); if ((min_span < uv_threshold) && (window2 != null) && (thisCorrelationSize < distortionParameters.maximalCorrelationSize)) { // trying to increase only // twice thisCorrelationSize *= 2; min_span *= 2; thisWindow = window2; if ((min_span < uv_threshold) && (window4 != null) && (thisCorrelationSize < distortionParameters.maximalCorrelationSize)) { thisCorrelationSize *= 2; min_span *= 2; thisWindow = window4; } } setCorrelationSizesUsed(thisCorrelationSize); if ((debug_level > (debug_threshold - 2)) && (thisCorrelationSize > distortionParameters.correlationSize)) System.out.println("**** u/v span too small, increasing FFT size to " + thisCorrelationSize); Rectangle centerCross = correlationSelection(beforeXY, // initial coordinates of the pattern cross point thisCorrelationSize); int ixc = centerCross.x + centerCross.width / 2; int iyc = centerCross.y + centerCross.height / 2; double[] diffBeforeXY = { beforeXY[0] - ixc, beforeXY[1] - iyc }; double[][] input_bayer = splitBayer(imp, centerCross, equalizeGreens); if (debug_level > (debug_threshold + 1)) ShowDoubleFloatArrays.showArrays(input_bayer, true, "centered"); if (debug_level > (debug_threshold + 0)) ShowDoubleFloatArrays.showArrays(input_bayer[4], "greens"); if (debug_level > (debug_threshold + 0)) System.out.println("ixc=" + ixc + " iyc=" + iyc); double[] greens = normalizeAndWindow(input_bayer[4], thisWindow); if (debug_level > (debug_threshold + 0)) ShowDoubleFloatArrays.showArrays(greens, "greensWindowed"); // average is not zero - probably if (debug_level > (debug_threshold + 0)) { System.out.println(" wv0x=" + IJ.d2s(wv0x, 5) + " wv0y=" + IJ.d2s(wv0y, 5)); System.out.println(" wv1x=" + IJ.d2s(wv1x, 5) + " wv1y=" + IJ.d2s(wv1y, 5)); System.out.println(" u-span=" + IJ.d2s(u_span, 3) + " v-span=" + IJ.d2s(v_span, 3) + " threshold=" + IJ.d2s(uv_threshold, 3) + " (" + IJ.d2s(distortionParameters.minUVSpan, 3) + ")"); if (corr != null) { System.out.println(" Ax=" + IJ.d2s(corr[0], 8) + " Bx=" + IJ.d2s(corr[1], 8) + " Cx=" + IJ.d2s(corr[2], 8) + " Dx=" + IJ.d2s(corr[6], 8) + " Ex=" + IJ.d2s(corr[7], 8)); System.out.println(" Ay=" + IJ.d2s(corr[3], 8) + " By=" + IJ.d2s(corr[4], 8) + " Cy=" + IJ.d2s(corr[5], 8) + " Dy=" + IJ.d2s(corr[8], 8) + " Ey=" + IJ.d2s(corr[9], 8)); } } int[][] greenNeib = { { 0, 0 }, { 0, 1 }, { 1, 0 }, { 1, 1 } }; int numOfNeib = distortionParameters.correlationAverageOnRefine ? greenNeib.length : 1; if (debug_level > (debug_threshold + 0)) { System.out.println(" numOfNeib=" + numOfNeib + " (distortionParameters.correlationAverageOnRefine=" + distortionParameters.correlationAverageOnRefine); } if (locsNeib.length == 1) { numOfNeib = 1; // on the first pass, from legacy if (debug_level > (debug_threshold + 0)) { System.out.println("Reduced numOfNeib to " + numOfNeib + " as locsNeib.length=" + locsNeib.length); } } if (dbgStr != null) { double dbgSumWindow = 0.0; for (double dbgD : thisWindow) dbgSumWindow += dbgD; // All he same - good/bad System.out.println(dbgStr + ": thisCorrelationSize=" + thisCorrelationSize + " min_span=" + min_span + " dbgSumWindow=" + dbgSumWindow + "locsNeib.length=" + locsNeib.length + " fast=" + fast + " numOfNeib=" + numOfNeib + " (distortionParameters.correlationAverageOnRefine=" + distortionParameters.correlationAverageOnRefine); } double[][] modelCorrs = new double[numOfNeib][]; double[][] modelCorrs_new = new double[numOfNeib][]; double[][] debugGreens = new double[numOfNeib][0]; for (numNeib = 0; numNeib < numOfNeib; numNeib++) { neibCenter[0] = diffBeforeXY[0] + 0.5 * (greenNeib[numNeib][0] + greenNeib[numNeib][1]); neibCenter[1] = diffBeforeXY[1] + 0.5 * (greenNeib[numNeib][0] - greenNeib[numNeib][1]); dUV = matrix2x2_scale(matrix2x2_mul(wv, neibCenter), -2 * Math.PI); double[] barray = simulationPattern.simulatePatternFullPatternSafe( // Is it the most time-consuming part? // should it be done once and then only // extraction separate? wv0x, wv0y, dUV[0] + (negative ? (-Math.PI / 2) : Math.PI / 2), // negative?(-Math.PI/2):Math.PI/2, wv1x, wv1y, dUV[1] + Math.PI / 2, // Math.PI/2, corr, // null, // no mesh distortion here thisSimulParameters.subdiv, // SIMUL.subdiv, - do not need high quality here thisCorrelationSize, true, // center for greens false);// boolean mono sim_pix = simulationPattern.extractSimulPatterns(barray, thisSimulParameters, 1, // subdivide output pixels thisCorrelationSize, // number of Bayer cells in width of the square selection (half number of // pixels) 0, 0); if (sim_pix == null) { System.out.println("***** BUG: extractSimulPatterns() FAILED *****"); return null; } if (dbgStr != null) { double dbgSumWindow = 0.0; for (double[] dbgSlice : sim_pix) for (double dbgD : dbgSlice) dbgSumWindow += dbgD; System.out.println(dbgStr + ": SUM of sim_pix=" + dbgSumWindow); // First difference good/bad } simGreensCentered = normalizeAndWindow(sim_pix[4], thisWindow); if (dbgStr != null) { double dbgSumWindow = 0.0; for (double dbgD : simGreensCentered) dbgSumWindow += dbgD; System.out.println(dbgStr + ": SUM of simGreensCentered=" + dbgSumWindow); } debugGreens[numNeib] = simGreensCentered.clone(); // testing if phase reversal would exactly inverse result pattern - tested, // perfect double[] simGreensCenteredClone = simGreensCentered.clone(); modelCorrs[numNeib] = fht_instance.correlate(greens.clone(), // measured pixel array // modelCorr=fht_instance.correlate (greens, // measured pixel array simGreensCentered, // simulated (model) pixel array) // distortionParameters.correlationHighPassSigma); distortionParameters.correlationHighPassSigma, (fast ? distortionParameters.correlationLowPassSigma : 0.0), // moved to decimation via FFT distortionParameters.phaseCorrelationFraction); modelCorrs_new[numNeib] = fht_instance.phaseCorrelate(greens.clone(), simGreensCenteredClone, patternDetectParameters.phaseCoeff, 0, // distortionParameters.correlationHighPassSigma, patternDetectParameters.lowpass_sigma, // (fast?distortionParameters.correlationLowPassSigma:0.0),// // moved to decimation via FFT null, null); if (dbgStr != null) { double dbgSumWindow = 0.0; for (double[] dbgSlice : modelCorrs) for (double dbgD : dbgSlice) dbgSumWindow += dbgD; System.out.println(dbgStr + ": SUM of modelCorrs=" + dbgSumWindow); } } if (debug_level > (debug_threshold + 0)) { System.out.println(">=========Showing simGreensCentered" + ixc + ":" + iyc); ShowDoubleFloatArrays.showArrays(debugGreens, true, "simGreensCentered" + ixc + ":" + iyc); } if (debug_level > (debug_threshold + 0)) { System.out.println(">=========Showing modelCorrs, passNumber=" + passNumber); ShowDoubleFloatArrays.showArrays(modelCorrs, true, "modelCorrs:" + numOfNeib); ShowDoubleFloatArrays.showArrays(modelCorrs_new, true, "modelCorrs_new:" + numOfNeib); } // combine 4 correlations into the double resolution, same output size (so half // input size) array int halfSize = thisCorrelationSize / 2; int qSize = thisCorrelationSize / 4; int thisFFTSubdiv = distortionParameters.correlationFFTSubdiv; double thisLowpass = distortionParameters.correlationLowPassSigma; double[] modelCorr; double[] modelCorr_new; if (numOfNeib > 1) { modelCorr = new double[thisCorrelationSize * thisCorrelationSize]; modelCorr_new = new double[thisCorrelationSize * thisCorrelationSize]; for (int i = 0; i < modelCorr.length; i++) modelCorr[i] = 0.0; for (int i = 0; i < modelCorr_new.length; i++) modelCorr_new[i] = 0.0; for (int dy = 0; dy < 2; dy++) for (int dx = 0; dx < 2; dx++) { for (int y = 0; y < halfSize; y++) for (int x = 0; x < halfSize; x++) { modelCorr[(2 * y + dy) * thisCorrelationSize + (2 * x + dx)] += modelCorrs[2 * dy + dx][(qSize + y) * thisCorrelationSize + (qSize + x)]; modelCorr_new[(2 * y + dy) * thisCorrelationSize + (2 * x + dx)] += modelCorrs_new[2 * dy + dx][(qSize + y) * thisCorrelationSize + (qSize + x)]; } } thisLowpass /= 2.0; // the lower the value, the more filtering. Decimated twice,so low pass // filtering - accordingly thisFFTSubdiv = (thisFFTSubdiv > 1) ? (thisFFTSubdiv / 2) : 1; } else { modelCorr = modelCorrs[0]; // also - different size modelCorr_new = modelCorrs_new[0]; // also - different size } if (debug_level > (debug_threshold + 0)) { System.out.println(">==========Showing modelCorr"); ShowDoubleFloatArrays.showArrays(modelCorr, thisCorrelationSize, thisCorrelationSize, "modelCorr"); } double[] centerXY_new; if (fast) centerXY_new = correlationMaximum( // maybe twice actual size if modelCorr_new, distortionParameters.correlationMaxOffset, (debug_level > (debug_threshold + 0)) && (numNeib == 0)); // low-pass filtering should already be // done else centerXY_new = correlationMaximum(modelCorr_new, distortionParameters.correlationRadius, distortionParameters.correlationThreshold, // double threshold, // fraction of maximum (slightly // less than 1.0) to limit the top part of the maximum // for centroid distortionParameters.correlationSubdiv, thisFFTSubdiv, fht_instance, distortionParameters.correlationMaxOffset, thisLowpass, // distortionParameters.correlationLowPassSigma // (debug_level>2) && (passNumber>1)); (debug_level > (debug_threshold + 0))); if (fast) centerXY = correlationMaximum( // maybe twice actual size if modelCorr, distortionParameters.correlationMaxOffset, (debug_level > (debug_threshold + 0)) && (numNeib == 0)); // low-pass filtering should already be // done else centerXY = correlationMaximum(modelCorr, distortionParameters.correlationRadius, distortionParameters.correlationThreshold, // double threshold, // fraction of maximum (slightly // less than 1.0) to limit the top part of the maximum // for centroid distortionParameters.correlationSubdiv, thisFFTSubdiv, fht_instance, distortionParameters.correlationMaxOffset, thisLowpass, // distortionParameters.correlationLowPassSigma // (debug_level>2) && (passNumber>1)); (debug_level > (debug_threshold + 0))); if (centerXY == null) { if (debug_level > (debug_threshold - 1)) System.out.println("Too far from the center01 (" + beforeXY[0] + "/" + beforeXY[1] + ")"); if (dbgStr != null) System.out.println(dbgStr + "- Too far from the center01 (" + beforeXY[0] + "/" + beforeXY[1] + ")"); return null; } // debug_level=3; if (numNeib > 1) { centerXY[0] *= 0.5; centerXY[1] *= 0.5; centerXY_new[0] *= 0.5; centerXY_new[1] *= 0.5; for (int i = 0; i < 2; i++) for (int j = 0; j < 2; j++) WVgreens[i][j] *= 0.5; } double[] contrasts_new = correlationContrast(modelCorr_new, greens, WVgreens, // wave vectors (same units as the // pixels array) distortionParameters.contrastSelectSigmaCenter, // Gaussian sigma to select correlation centers (pixels, // 2.0) distortionParameters.contrastSelectSigma, // Gaussian sigma to select correlation centers (fraction of // UV period), 0.1 centerXY[0], // x0, // center coordinates centerXY[1], // y0, "test-contrast-new"); // title base for optional plots names double[] contrasts = correlationContrast(modelCorr, greens, WVgreens, // wave vectors (same units as the pixels // array) distortionParameters.contrastSelectSigmaCenter, // Gaussian sigma to select correlation (pixels, 2.0) distortionParameters.contrastSelectSigma, // Gaussian sigma to select correlation centers (fraction of // UV period), 0.1 centerXY[0], // x0, // center coordinates centerXY[1], // y0, "test-contrast"); // title base for optional plots names if ((debug_level > (debug_threshold - 1))) { System.out.println("contrast_new = " + contrasts_new[0] + ", contrast = " + contrasts[0]); } contrast = contrasts[0]; result[2] = contrast; if (Double.isNaN(contrasts[0]) || ((distortionParameters.correlationMinContrast > 0) && (contrasts[0] < distortionParameters.correlationMinContrast))) { if ((debug_level > (debug_threshold - 1))) System.out.println( "Contrast too low - " + contrasts[0] + "<" + distortionParameters.correlationMinContrast); if (debug_level > (debug_threshold - 1)) System.out.println("Contrast " + IJ.d2s(contrasts[0], 3) + " (" + distortionParameters.correlationMinContrast + ")" + " is TOO LOW (" + IJ.d2s(beforeXY[0], 3) + "/" + IJ.d2s(beforeXY[1], 3) + ")->" + IJ.d2s(centerXY[0], 3) + "/" + IJ.d2s(centerXY[1], 3)); if (dbgStr != null) System.out.println(dbgStr + " - Contrast " + IJ.d2s(contrasts[0], 3) + " (" + distortionParameters.correlationMinContrast + ")" + " is TOO LOW (" + IJ.d2s(beforeXY[0], 3) + "/" + IJ.d2s(beforeXY[1], 3) + ")->" + IJ.d2s(centerXY[0], 3) + "/" + IJ.d2s(centerXY[1], 3)); return null; } else { if (debug_level > (debug_threshold - 1)) System.out.println("Contrast " + IJ.d2s(contrasts[0], 3) + " (" + distortionParameters.correlationMinContrast + ")" + " is GOOD (" + IJ.d2s(beforeXY[0], 3) + "/" + IJ.d2s(beforeXY[1], 3) + ")->" + IJ.d2s(centerXY[0], 3) + "/" + IJ.d2s(centerXY[1], 3)); if (dbgStr != null) System.out.println(dbgStr + " - Contrast " + IJ.d2s(contrasts[0], 3) + " (" + distortionParameters.correlationMinContrast + ")" + " is GOOD (" + IJ.d2s(beforeXY[0], 3) + "/" + IJ.d2s(beforeXY[1], 3) + ")->" + IJ.d2s(centerXY[0], 3) + "/" + IJ.d2s(centerXY[1], 3)); } if (Double.isNaN(contrasts[1]) || ((distortionParameters.correlationMinAbsoluteContrast > 0) && (contrasts[1] < distortionParameters.correlationMinAbsoluteContrast))) { if (debug_level > (debug_threshold - 1)) System.out.println("Absolute contrast too low - " + contrasts[1] + "<" + distortionParameters.correlationMinAbsoluteContrast); if (debug_level > (debug_threshold - 1)) System.out.println("Absolute contrast " + IJ.d2s(contrasts[1], 3) + " (" + distortionParameters.correlationMinAbsoluteContrast + ")" + " is too low (" + IJ.d2s(beforeXY[0], 3) + "/" + IJ.d2s(beforeXY[1], 3) + ")->" + IJ.d2s(centerXY[0], 3) + "/" + IJ.d2s(centerXY[1], 3)); if (dbgStr != null) System.out.println(dbgStr + " - Absolute contrast " + IJ.d2s(contrasts[1], 3) + " (" + distortionParameters.correlationMinAbsoluteContrast + ")" + " is too low (" + IJ.d2s(beforeXY[0], 3) + "/" + IJ.d2s(beforeXY[1], 3) + ")->" + IJ.d2s(centerXY[0], 3) + "/" + IJ.d2s(centerXY[1], 3)); return null; } else { if (dbgStr != null) System.out.println(dbgStr + " - Absolute contrast " + IJ.d2s(contrasts[1], 3) + " (" + distortionParameters.correlationMinAbsoluteContrast + ")" + " is GOOD (" + IJ.d2s(beforeXY[0], 3) + "/" + IJ.d2s(beforeXY[1], 3) + ")->" + IJ.d2s(centerXY[0], 3) + "/" + IJ.d2s(centerXY[1], 3)); } if (debug_level > (debug_threshold - 0)) System.out.println(">>>Contrast=" + contrasts[0] + "/" + contrasts[1] + " (" + IJ.d2s(beforeXY[0], 3) + ":" + IJ.d2s(beforeXY[1], 3) + ")->" + IJ.d2s(result[0], 3) + ":" + IJ.d2s(result[1], 3)); result[0] = ixc - (-centerXY[0] - centerXY[1]) + diffBeforeXY[0]; result[1] = iyc - (centerXY[0] - centerXY[1]) + diffBeforeXY[1]; if (debug_level > (debug_threshold + 0)) System.out.println(">---correctedPatternCrossLocation: before x=" + IJ.d2s(beforeXY[0], 3) + " y=" + IJ.d2s(beforeXY[1], 3)); if (debug_level > (debug_threshold + 0)) System.out.println(">+++correctedPatternCrossLocation: after x=" + IJ.d2s(result[0], 3) + " y=" + IJ.d2s(result[1], 3)); return result; } /* * ======= Debugging only - returns 2-d array of x,y as a function of initial * estimation =================== */ public double[][][] scanPatternCrossLocation(double range, // size of the scanning square int size, // number of scan points in each direction (total size*size) double[] beforeCenterXY, // initial coordinates of the pattern cross point double wv0x, double wv0y, double wv1x, double wv1y, ImagePlus imp, // image data (Bayer mosaic) DistortionParameters distortionParameters, // MatchSimulatedPattern.PatternDetectParameters patternDetectParameters, MatchSimulatedPattern matchSimulatedPattern, // correlationSize SimulationPattern.SimulParameters thisSimulParameters, boolean equalizeGreens, double[] window, // window // function SimulationPattern simulationPattern, boolean negative, // invert cross phase DoubleFHT fht_instance) { // double [] result=beforeXY.clone(); double[][][] result = new double[size][size][4]; if (fht_instance == null) fht_instance = new DoubleFHT(); // move upstream to reduce number of initializations double[] beforeXY = new double[2]; double[] filter = fht_instance.createFrequencyFilter( new double[distortionParameters.correlationSize * distortionParameters.correlationSize], // distortionParameters.correlationSize, distortionParameters.correlationHighPassSigma, distortionParameters.correlationLowPassSigma); if (debugLevel > 2) { double[] maskFull = new double[distortionParameters.correlationSize * distortionParameters.correlationSize]; for (int i = 0; i < maskFull.length; i++) { if (i < filter.length) maskFull[i] = filter[i]; else { int rowMod = (distortionParameters.correlationSize - (i / distortionParameters.correlationSize)) % distortionParameters.correlationSize; int colMod = (distortionParameters.correlationSize - (i % distortionParameters.correlationSize)) % distortionParameters.correlationSize; maskFull[i] = filter[rowMod * distortionParameters.correlationSize + colMod]; } } ShowDoubleFloatArrays.showArrays(maskFull, "filter"); } for (int i = 0; i < size; i++) for (int j = 0; j < size; j++) { beforeXY[1] = beforeCenterXY[1] - range / 2 + (range * i) / (size - 1); beforeXY[0] = beforeCenterXY[0] - range / 2 + (range * j) / (size - 1); Rectangle centerCross = correlationSelection(beforeXY, // initial coordinates of the pattern cross point distortionParameters.correlationSize); int ixc = centerCross.x + centerCross.width / 2; int iyc = centerCross.y + centerCross.height / 2; double[] diffBeforeXY = { beforeXY[0] - ixc, beforeXY[1] - iyc }; // create diagonal green selection around ixc,iyc double[][] input_bayer = splitBayer(imp, centerCross, equalizeGreens); /* * double[][] corrWindow=null; if * (distortionParameters.correlationRadiusScale>=0.0) { * corrWindow=generateWeights ( distortionParameters.correlationWeightSigma, * distortionParameters.correlationRadiusScale); // if 0 - use sigma as radius, * inside - 1.0, outside 0.0. If >0 - size of array n*sigma * * } * */ if (debugLevel > 3) ShowDoubleFloatArrays.showArrays(input_bayer, true, "centered"); if (debugLevel > 1) System.out.println(i + "/" + j + ": ixc=" + ixc + " iyc=" + iyc); // alternative way to generate shifted pattern double[][] wv = { { wv0x, wv0y }, { wv1x, wv1y } }; double[] dUV = matrix2x2_scale(matrix2x2_mul(wv, diffBeforeXY), -2 * Math.PI); // correlationHighPassSigma double[] greens = normalizeAndWindow(input_bayer[4], window); simulationPattern.simulatePatternFullPattern(wv0x, wv0y, dUV[0] + (negative ? (-Math.PI / 2) : Math.PI / 2), wv1x, wv1y, dUV[1] + Math.PI / 2, // 0.0, null, // no mesh distortion here thisSimulParameters.subdiv, // SIMUL.subdiv, - do not need high quality here distortionParameters.correlationSize, true, // center for greens false);// boolean mono double[][] sim_pix = simulationPattern.extractSimulPatterns(thisSimulParameters, 1, // subdivide output // pixels distortionParameters.correlationSize, // number of Bayer cells in width of the square selection // (half number of pixels) 0.0, // -diffBeforeXY[0], 0.0); // -diffBeforeXY[1]); double[] simGreensCentered = normalizeAndWindow(sim_pix[4], window); if (debugLevel > 2) ShowDoubleFloatArrays.showArrays(greens.clone(), "greens-i" + i + "-j" + j); if (debugLevel > 2) ShowDoubleFloatArrays.showArrays(simGreensCentered.clone(), "simGreensCentered-i" + i + "-j" + j); double[] modelCorr = fht_instance.correlate(greens, // measured pixel array simGreensCentered, // simulated (model) pixel array) // distortionParameters.correlationHighPassSigma); filter); if (debugLevel > 2) ShowDoubleFloatArrays.showArrays(modelCorr.clone(), "modelCorr-i" + i + "-j" + j); double[] xyCorr = new double[2]; double[] centerXY; // if (distortionParameters.correlationRadiusScale>=0.0) centerXY= // correlationMaximum(modelCorr,corrWindow); if (distortionParameters.correlationRadius > 0) { centerXY = correlationMaximum(modelCorr, distortionParameters.correlationRadius, distortionParameters.correlationThreshold, distortionParameters.correlationSubdiv, distortionParameters.correlationFFTSubdiv, fht_instance, distortionParameters.correlationMaxOffset, 0.0, // low-pass filtering already done (debugLevel > 2)); } else centerXY = correlationMaximum(modelCorr, distortionParameters.correlationMaxOffset, (debugLevel > 2)); if (centerXY == null) { centerXY = new double[2]; centerXY[0] = 0.0; centerXY[1] = 0.0; } if (debugLevel > 2) System.out.println("correctedPatternCrossLocation: Center x=" + IJ.d2s(centerXY[0], 3) + " y=" + IJ.d2s(centerXY[1], 3)); xyCorr[0] = -centerXY[0] - centerXY[1]; xyCorr[1] = centerXY[0] - centerXY[1]; if (debugLevel > 1) System.out.println("correctedPatternCrossLocation: " + i + "/" + j + ": dist=" + IJ.d2s(Math.sqrt(xyCorr[0] * xyCorr[0] + xyCorr[1] * xyCorr[1]), 4) + " xyCorr[0]=" + IJ.d2s(xyCorr[0], 4) + " xyCorr[1]=" + IJ.d2s(xyCorr[1], 4)); // result[0]=ixc-xyCorr[0]; // result[1]=iyc-xyCorr[1]; result[i][j][0] = ixc - xyCorr[0] + diffBeforeXY[0]; result[i][j][1] = iyc - xyCorr[1] + diffBeforeXY[1]; result[i][j][2] = beforeXY[0]; result[i][j][3] = beforeXY[1]; if (debugLevel > 1) System.out.println("---correctedPatternCrossLocation: " + i + "/" + j + " before x=" + IJ.d2s(beforeXY[0], 3) + " y=" + IJ.d2s(beforeXY[1], 3)); if (debugLevel > 1) System.out.println("+++correctedPatternCrossLocation: " + i + "/" + j + " after x=" + IJ.d2s(result[i][j][0], 3) + " y=" + IJ.d2s(result[i][j][1], 3)); } return result; } /* * distortionParameters.correlationWeightSigma= gd.getNextNumber(); * distortionParameters.correlationRadiusScale= gd.getNextNumber(); * */ /* ======================================================================== */ /** * Interpolate maximum on a square correlation array, return vector from the * center */ // one quater of the weights function to be used to approximate maximum on // correlation by a second-degree polynominal public double[][] generateWeights(double sigma, double n) { // if 0 - use sigma as radius, inside - 1.0, outside // 0.0. If >0 - size of array n*sigma double r0 = ((n > 0) ? n : 1.0) * sigma; double r2 = r0 * r0; int size = (int) Math.ceil(r0); double[][] mask = new double[size][size]; int i, j; double[] gaussian = new double[size]; if (n > 0) { double k = 0.5 / sigma / sigma; for (i = 0; i < size; i++) gaussian[i] = Math.exp(-(k * i * i)); } for (i = 0; i < size; i++) for (j = 0; j < size; j++) { if ((i * i + j * j) > r2) mask[i][j] = 0.0; else if (n > 0) mask[i][j] = gaussian[i] * gaussian[j]; else mask[i][j] = 1.0; } return mask; } public double[] correlationMaximum(double[] corr, // square (correlation) array to find location of the maximum // ([0.0,0.0] in the center of the arrray) double[][] weights, double maxOffset) { if ((corr == null) || (corr.length == 0)) return null; // double [] corrMax= new double[2]; int size = (int) Math.sqrt(corr.length); int i, j, imax = 0, ix, iy, ixc, iyc; double max = corr[0]; for (i = 1; i < corr.length; i++) if (max < corr[i]) { max = corr[i]; imax = i; } iyc = imax / size; ixc = imax % size; int ixc0 = ixc - size / 2; int iyc0 = iyc - size / 2; if ((maxOffset > 0) && (maxOffset * maxOffset < (ixc0 * ixc0 + iyc0 * iyc0))) { if (debugLevel > 1) System.out.println("Too far from the center1: ixc=" + ixc + " iyc=" + iyc); return null; } if (debugLevel > 1) System.out.println("correlationMaximum: ixc=" + ixc + " iyc=" + iyc); /* * ix, iy - the location of the point with maximal value. We'll approximate the * vicinity of that maximum using a second degree polunominal: * Z(x,y)~=A*x^2+B*y^2+C*x*y+D*x+E*y+F by minimizing sum of squared differences * between the actual (Z(x,uy)) and approximated values. and then find the * maximum on the approximated surface. Here is the math: * * Z(x,y)~=A*x^2+B*y^2+C*x*y+D*x+E*y+F minimizing squared error, using W(x,y) as * weight function * * error=Sum(W(x,y)*((A*x^2+B*y^2+C*x*y+D*x+E*y+F)-Z(x,y))^2) * * error=Sum(W(x,y)*(A^2*x^4 + 2*A*x^2*(B*y^2+C*x*y+D*x+E*y+F-Z(x,y)) +(...) ) * 0=derror/dA=Sum(W(x,y)*(2*A*x^4 + 2*x^2*(B*y^2+C*x*y+D*x+E*y+F-Z(x,y))) * 0=Sum(W(x,y)*(A*x^4 + x^2*(B*y^2+C*x*y+D*x+E*y+F-Z(x,y))) * * SX4=Sum(W(x,y)*x^4), etc * * (1) 0=A*SX4 + B*SX2Y2 + C*SX3Y +D*SX3 +E*SX2Y +F*SX2 - SZX2 * * derror/dB: * * error=Sum(W(x,y)*(B^2*y^4 + 2*B*y^2*(A*x^2+C*x*y+D*x+E*y+F-Z(x,y)) +(...) ) * 0=derror/dB=Sum(W(x,y)*(2*B*y^4 + 2*y^2*(A*x^2+C*x*y+D*x+E*y+F-Z(x,y))) * 0=Sum(W(x,y)*(B*y^4 + y^2*(A*x^2+C*x*y+D*x+E*y+F-Z(x,y))) * * (2) 0=B*SY4 + A*SX2Y2 + C*SXY3 +D*SXY2 +E*SY3 +F*SY2 - SZY2 (2) 0=A*SX2Y2 + * B*SY4 + C*SXY3 +D*SXY2 +E*SY3 +F*SY2 - SZY2 * * derror/dC: * * error=Sum(W(x,y)*(C^2*x^2*y^2 + 2*C*x*y*(A*x^2+B*y^2+D*x+E*y+F-Z(x,y)) +(...) * ) 0=derror/dC=Sum(W(x,y)*(2*C*x^2*y^2 + 2*x*y*(A*x^2+B*y^2+D*x+E*y+F-Z(x,y)) * ) 0=Sum(W(x,y)*(C*x^2*y^2 + x*y*(A*x^2+B*y^2+D*x+E*y+F-Z(x,y)) ) * * (3) 0= A*SX3Y + B*SXY3 + C*SX2Y2 + D*SX2Y + E*SXY2 + F*SXY - SZXY * * derror/dD: * * error=Sum(W(x,y)*(D^2*x^2 + 2*D*x*(A*x^2+B*y^2+C*x*y+E*y+F-Z(x,y)) +(...) ) * 0=derror/dD=Sum(W(x,y)*(2*D*x^2 + 2*x*(A*x^2+B*y^2+C*x*y+E*y+F-Z(x,y)) ) * 0=Sum(W(x,y)*(D*x^2 + x*(A*x^2+B*y^2+C*x*y+E*y+F-Z(x,y)) ) * * (4) 0= A*SX3 + B*SXY2 + C*SX2Y + D*SX2 + E*SXY + F*SX - SZX * * derror/dE: * * error=Sum(W(x,y)*(E^2*y^2 + 2*E*y*(A*x^2+B*y^2+C*x*y+D*x+F-Z(x,y)) +(...) ) * 0=derror/dE=Sum(W(x,y)*(2*E*y^2 + 2*y*(A*x^2+B*y^2+C*x*y+D*x+F-Z(x,y)) ) * 0=Sum(W(x,y)*(E*y^2 + y*(A*x^2+B*y^2+C*x*y+D*x+F-Z(x,y)) ) (5) 0= A*SX2Y + * B*SY3 + C*SXY2 + D*SXY + E*SY2 + F*SY - SZY * * derror/dF: * * error=Sum(W(x,y)*(F^2 + 2*F*(A*x^2+B*y^2+C*x*y+D*x+E*y-Z(x,y)) +(...) ) * 0=derror/dF=Sum(W(x,y)*(2*F + 2*(A*x^2+B*y^2+C*x*y+D*x+E*y-Z(x,y)) ) * 0=Sum(W(x,y)*(F + (A*x^2+B*y^2+C*x*y+D*x+E*y-Z(x,y)) ) (6) 0= A*SX2 + B*SY2 + * C*SXY + D*SX + E*SY + F*S - SZ * * * * * (1) 0= A*SX4 + B*SX2Y2 + C*SX3Y + D*SX3 + E*SX2Y + F*SX2 - SZX2 (2) 0= * A*SX2Y2 + B*SY4 + C*SXY3 + D*SXY2 + E*SY3 + F*SY2 - SZY2 (3) 0= A*SX3Y + * B*SXY3 + C*SX2Y2 + D*SX2Y + E*SXY2 + F*SXY - SZXY (4) 0= A*SX3 + B*SXY2 + * C*SX2Y + D*SX2 + E*SXY + F*SX - SZX (5) 0= A*SX2Y + B*SY3 + C*SXY2 + D*SXY + * E*SY2 + F*SY - SZY (6) 0= A*SX2 + B*SY2 + C*SXY + D*SX + E*SY + F*S - SZ * * * (1) 0= A*S40 + B*S22 + C*S31 + D*S30 + E*S21 + F*S20 - SZ20 (2) 0= A*S22 + * B*S04 + C*S13 + D*S12 + E*S03 + F*S02 - SZ02 (3) 0= A*S31 + B*S13 + C*S22 + * D*S21 + E*S12 + F*S11 - SZ11 (4) 0= A*S30 + B*S12 + C*S21 + D*S20 + E*S11 + * F*S10 - SZ10 (5) 0= A*S21 + B*S03 + C*S12 + D*S11 + E*S02 + F*S01 - SZ01 (6) * 0= A*S20 + B*S02 + C*S11 + D*S10 + E*S01 + F*S00 - SZ00 * * * we beed x,y of maximum, so d(A*x^2+B*y^2+C*x*y+D*x+E*y+F)/dx=0 * d(A*x^2+B*y^2+C*x*y+D*x+E*y+F)/dy=0 d()/dx=2*A*x+C*y+D=0 d()/dy=C*x+2*B*y+E=0 * * * | S40 S22 S31 S30 S21 S20 | | A | | SZ20 | | S22 S04 S13 S12 S03 S02 | | B | * | SZ02 | | S31 S13 S22 S21 S12 S11 | | C | | SZ11 | | S30 S12 S21 S20 S11 S10 * | * | D | = | SZ10 | | S21 S03 S12 S11 S02 S01 | | E | | SZ01 | | S20 S02 S11 * S10 S01 S00 | | F | | SZ00 | * * * | 2*A C | * | x | = | -D | | C 2*B | | Y | | -E | * */ double S00 = 0.0, S10 = 0.0, S01 = 0.0, S20 = 0.0, S11 = 0.0, S02 = 0.0, S30 = 0.0, S21 = 0.0, S12 = 0.0, S03 = 0.0, S40 = 0.0, S31 = 0.0, S22 = 0.0, S13 = 0.0, S04 = 0.0, SZ00 = 0.0, SZ10 = 0.0, SZ01 = 0.0, SZ20 = 0.0, SZ11 = 0.0, SZ02 = 0.0; int wsize = weights.length; double w, z, x, x2, x3, x4, y, y2, y3, y4, wz; for (i = iyc - wsize + 1; i < iyc + wsize; i++) if ((i > 0) && (i < size)) for (j = ixc - wsize + 1; j < ixc + wsize; j++) if ((j > 0) && (j < size)) { iy = i - iyc; ix = j - ixc; w = weights[(iy >= 0) ? iy : -iy][(ix >= 0) ? ix : -ix]; if (w > 0) { z = corr[i * size + j]; wz = w * z; x = ix; x2 = x * x; x3 = x2 * x; x4 = x3 * x; y = iy; y2 = y * y; y3 = y2 * y; y4 = y3 * y; S00 += w; S10 += w * x; S01 += w * y; S20 += w * x2; S11 += w * x * y; S02 += w * y2; S30 += w * x3; S21 += w * x2 * y; S12 += w * x * y2; S03 += w * y3; S40 += w * x4; S31 += w * x3 * y; S22 += w * x2 * y2; S13 += w * x * y3; S04 += w * y4; SZ00 += wz; SZ10 += wz * x; SZ01 += wz * y; SZ20 += wz * x2; SZ11 += wz * x * y; SZ02 += wz * y2; } } /* * | S40 S22 S31 S30 S21 S20 | | A | | SZ20 | | S22 S04 S13 S12 S03 S02 | | B | * | SZ02 | | S31 S13 S22 S21 S12 S11 | | C | | SZ11 | | S30 S12 S21 S20 S11 S10 * | * | D | = | SZ10 | | S21 S03 S12 S11 S02 S01 | | E | | SZ01 | | S20 S02 S11 * S10 S01 S00 | | F | | SZ00 | * */ double[][] mAarray = { { S40, S22, S31, S30, S21, S20 }, { S22, S04, S13, S12, S03, S02 }, { S31, S13, S22, S21, S12, S11 }, { S30, S12, S21, S20, S11, S10 }, { S21, S03, S12, S11, S02, S01 }, { S20, S02, S11, S10, S01, S00 } }; double[] zAarray = { SZ20, SZ02, SZ11, SZ10, SZ01, SZ00 }; Matrix M = new Matrix(mAarray); Matrix Z = new Matrix(zAarray, 6); double[] ABCDEF = M.solve(Z).getRowPackedCopy(); /* * | 2*A C | * | x | = | -D | | C 2*B | | Y | | -E | */ double[][] mXYarray = { { 2 * ABCDEF[0], ABCDEF[2] }, { ABCDEF[2], 2 * ABCDEF[1] } }; double[] mDEarray = { -ABCDEF[3], -ABCDEF[4] }; Matrix mXY = new Matrix(mXYarray); Matrix mDE = new Matrix(mDEarray, 2); double[] corrMax = mXY.solve(mDE).getRowPackedCopy(); if (debugLevel > 1) System.out.println("correlationMaximum: ixc=" + ixc + " iyc=" + iyc + " corrMax[0]=" + corrMax[0] + " corrMax[1]=" + corrMax[1]); corrMax[0] += ixc - size / 2; corrMax[1] += iyc - size / 2; if (debugLevel > 2) { double[] approx = new double[size * size]; for (i = 0; i < approx.length; i++) approx[i] = 0.0; for (i = iyc - wsize + 1; i < iyc + wsize; i++) if ((i > 0) && (i < size)) for (j = ixc - wsize + 1; j < ixc + wsize; j++) if ((j > 0) && (j < size)) { iy = i - iyc; ix = j - ixc; x = ix; y = iy; // z=corr[i*size+j]; approx[i * size + j] = ABCDEF[0] * x * x + ABCDEF[1] * y * y + ABCDEF[2] * x * y + ABCDEF[3] * x + ABCDEF[4] * y + ABCDEF[5]; } double[][] both = new double[2][]; both[0] = corr; both[1] = approx; // corr ShowDoubleFloatArrays.showArrays(both, true, "corr-approx"); // stack } // if (debugLevel>2) System.out.println("correlationMaximum: ix="+ix+" iy="+iy); // if (debugLevel>2) System.out.println("correlationMaximum: maxInHor[0] // ="+maxInHor[0]+ " maxInHor[1]= "+maxInHor[1]+ " maxInHor[2]= "+maxInHor[2]); // if (debugLevel>2) System.out.println("correlationMaximum: // maxInVert[0]="+maxInVert[0]+" maxInVert[1]="+maxInVert[1]+" // maxInVert[2]="+maxInVert[2]); return corrMax; } private double[] correlationMaximum(double[] corr, int dist, // maximal distance from the maximum to consider double threshold, // fraction of maximum (slightly less than 1.0) to limit the top part of the // maximum for centroid int decimate, // interpolate to finer grid (both FFT and linear) int decimateFFT, // should be power of 2 DoubleFHT fht_instance, double maxOffset, double lowpass, // relative to original corr size (will be scaled // for decimation). Will only be applied if // decimateFFT >1! boolean showDebug) { if ((corr == null) || (corr.length == 0)) return null; int size = (int) Math.sqrt(corr.length); int i, j, imax = 0, ixc, iyc, index; // if (showDebug) System.out.println("correlationMaximum(), // decimateFFT="+decimateFFT); /** * Reduces size of the correlation area (using center part) and simultaneously * interpolating pixels, so the result is a scaled version of the center (total * FFT suize remains the same) */ if (showDebug) { System.out.println("correlationMaximum(): decimate=" + decimate + " decimateFFT=" + decimateFFT); } if (decimateFFT > 1) { if (fht_instance == null) fht_instance = new DoubleFHT(); double scale = decimateFFT * decimateFFT; double[] corr1 = new double[corr.length]; /** * As we are interested only in the center part of the image, we'll use flat-top * window based on Hamming. */ int size1 = size / decimateFFT; for (i = 0; i < corr1.length; i++) corr1[i] = 0.0; double borderAverage = 0; index = size * (size + 1) * (decimateFFT - 1) / decimateFFT / 2; int i0 = index, i1 = index + size1, i2 = i1 + size1 * size, i3 = i2 - size1; // on right and bottom edge // goes 1 pixel outside of // the used area for (i = 0; i < size1; i++) { /* * if (showDebug){ System.out.println(":: size="+size+" size1="+size1+ * " i="+i+" i0="+i0+" i1="+i1+" i2="+i2+" i3="+i3+" scale="+scale); } */ borderAverage += corr[i0++] + corr[i1 += size1] + corr[i2--] + corr[i3 -= size1]; } borderAverage /= 4 * size1; double[] preHammingMod = fht_instance.getHamming1d(size1 / 2); double[] hammingMod = new double[size1]; for (i = 0; i < size1 / 4; i++) hammingMod[i] = preHammingMod[i]; for (i = 1; i < size1 / 4; i++) hammingMod[size1 - i] = preHammingMod[i]; for (i = size1 / 4; i <= (size1 - size1 / 4); i++) hammingMod[i] = 1.0; if (showDebug) System.out.println("scale=" + scale + " borderAverage=" + borderAverage); for (i = 0; i < size1; i++) for (j = 0; j < size1; j++) { corr1[(i * size + j) * decimateFFT] = scale * (corr[index + i * size + j] - borderAverage) * hammingMod[i] * hammingMod[j] + borderAverage; } if (showDebug) ShowDoubleFloatArrays.showArrays(corr1.clone(), "decimatedForFFT"); fht_instance.swapQuadrants(corr1); if (!fht_instance.transform(corr1, false)) return null; // direct FHT if (showDebug) ShowDoubleFloatArrays.showArrays(corr1.clone(), "FFT"); // zero out aliases for (i = 0; i <= size1 / 2; i++) for (j = size1 / 2 + 1; j < size - (size1 / 2); j++) corr1[i * size + j] = 0.0; for (i = size1 / 2 + 1; i < size - (size1 / 2); i++) for (j = 0; j < size; j++) corr1[i * size + j] = 0.0; for (i = size - (size1 / 2); i < size; i++) for (j = size1 / 2 + 1; j < size - (size1 / 2); j++) corr1[i * size + j] = 0.0; // apply window for now - just /* * if (showDebug) { System.out.println("Getting hamming1d ("+size1+")"); } */ double[] hamming = fht_instance.getHamming1d(size1).clone(); /* * if (showDebug) { for (i=0;i<hamming.length;i++) * System.out.println("hamming["+i+"]="+hamming[i]); } if (showDebug) * ShowDoubleFloatArrays.showArrays(corr1.clone(), "NO_ALIAS"); // Combine with low-pass * Gaussian (if it is >0) if (lowpass>0){ double [] * gaussian1d=fht_instance.getGaussian1d(lowpass,size1); // no need to divide by * /decimateFFT as we use size1, not size for (i=0;i<hammingMod.length;i++) * hamming[i]*=gaussian1d[i]; if (showDebug) { * System.out.println("lowpass="+lowpass); for (i=0;i<gaussian1d.length;i++) * System.out.println("gaussian1d["+i+"]="+gaussian1d[i]); } } * * if (showDebug) { for (i=0;i<hamming.length;i++) * System.out.println("hamming["+i+"]="+hamming[i]); } */ int halfSize1 = size1 / 2, shiftZero = size - halfSize1; for (i = 0; i <= size1; i++) for (j = 0; j <= size1; j++) { int im = i % size1, jm = j % size1; corr1[((i + shiftZero) % size) * size + ((j + shiftZero) % size)] *= hamming[im] * hamming[jm]; } if (showDebug) ShowDoubleFloatArrays.showArrays(corr1.clone(), "FFT-masked"); if (!fht_instance.transform(corr1, true)) return null; // inverse FHT fht_instance.swapQuadrants(corr1); if (showDebug) ShowDoubleFloatArrays.showArrays(corr1.clone(), "decimatedAfterFFT"); dist *= decimateFFT; maxOffset *= decimateFFT; decimate /= decimateFFT; corr = corr1; // replace } double max = corr[0]; for (i = 1; i < corr.length; i++) if (max < corr[i]) { max = corr[i]; imax = i; } iyc = imax / size; ixc = imax % size; int ixc0 = ixc - size / 2; int iyc0 = iyc - size / 2; if ((maxOffset > 0) && (maxOffset * maxOffset < (ixc0 * ixc0 + iyc0 * iyc0))) { if (showDebug || (debugLevel > 1)) System.out.println("Too far from the center2: ixc=" + ixc + " iyc=" + iyc + " ixc0=" + ixc0 + " iyc0=" + iyc0 + " maxOffset=" + maxOffset); // if (showDebug || (debugLevel>0)) System.out.println("Too far from the // center2: ixc="+ixc+" iyc="+iyc+" ixc0="+ixc0+" iyc0="+iyc0+" // maxOffset="+maxOffset); return null; } if (showDebug || (debugLevel > 1)) System.out.println("correlationMaximum: ixc=" + ixc + " iyc=" + iyc + " ixc0=" + ixc0 + " iyc0=" + iyc0 + " maxOffset=" + maxOffset); // reduce dist if it hits borders if (dist > iyc) dist = iyc; if (dist > ixc) dist = ixc; if (dist > (size - iyc - 1)) dist = (size - iyc - 1); if (dist > (size - ixc - 1)) dist = (size - ixc - 1); int interpSize = 2 * dist * decimate + 1; double[][] cell = new double[2][2]; double[] row = new double[2]; double[] ki = new double[2]; double kj; double[] interpCorr = new double[interpSize * interpSize]; int i1, j1; int i1Range, j1Range; for (i = 0; i < 2 * dist; i++) for (j = 0; j < 2 * dist; j++) { index = (iyc - dist + i) * size + (ixc - dist + j); cell[0][0] = corr[index]; cell[0][1] = corr[index + 1]; cell[1][0] = corr[index + size]; cell[1][1] = corr[index + size + 1]; i1Range = decimate + ((i == (2 * dist - 1)) ? 1 : 0); j1Range = decimate + ((j == (2 * dist - 1)) ? 1 : 0); ki[0] = (cell[1][0] - cell[0][0]) / decimate; ki[1] = (cell[1][1] - cell[0][1]) / decimate; for (i1 = 0; i1 < i1Range; i1++) { row[0] = cell[0][0] + ki[0] * i1; row[1] = cell[0][1] + ki[1] * i1; kj = (row[1] - row[0]) / decimate; for (j1 = 0; j1 < j1Range; j1++) { interpCorr[(i * decimate + i1) * interpSize + (j * decimate + j1)] = row[0] + kj * j1; } } } // Gaussian blur the after linear interpolation, use sigma = 0.75* decimate ? // now find the maximal value on the border - it will be a threshold for a wave // from the center double interpolationBlurSigma = 0.75 * decimate; DoubleGaussianBlur gb = new DoubleGaussianBlur(); gb.blurDouble(interpCorr, interpSize, interpSize, interpolationBlurSigma, interpolationBlurSigma, 0.01); double limit = interpCorr[0]; for (i = 0; i < interpSize; i++) { if (limit < interpCorr[i]) limit = interpCorr[i]; if (limit < interpCorr[interpSize * interpSize - i - 1]) limit = interpCorr[interpSize * interpSize - i - 1]; if (limit < interpCorr[interpSize * i]) limit = interpCorr[interpSize * i]; if (limit < interpCorr[interpSize * i + (interpSize - 1)]) limit = interpCorr[interpSize * i + (interpSize - 1)]; } // Now modify the limit if it is below threshold*max (sharp maximum) if (limit < threshold * max) limit = threshold * max; // run wave from the center, border pixels <=limit, so no need to verify array // limits List<Integer> pixelList = new ArrayList<Integer>(100); Integer Index, newIndex; int[] clusterMap = new int[interpSize * interpSize]; for (i = 0; i < clusterMap.length; i++) clusterMap[i] = 0; int[] dirs = { -1, -interpSize - 1, -interpSize, -interpSize + 1, 1, interpSize + 1, interpSize, interpSize - 1 }; Index = dist * decimate * (interpSize + 1); // center pixelList.clear(); pixelList.add(Index); if (showDebug || (debugLevel > 1)) System.out.println("correlationMaximum: pixelList.add (" + Index + "), i=" + (Index / interpSize) + " j= " + (Index % interpSize)); clusterMap[Index] = 1; while (pixelList.size() > 0) { Index = pixelList.remove(0); for (i = 0; i < dirs.length; i++) { newIndex = Index + dirs[i]; if ((clusterMap[newIndex] == 0) && (interpCorr[newIndex] > limit)) { pixelList.add(newIndex); clusterMap[newIndex] = 1; } } } // Calculate centroid double s = 0.0, sx = 0.0, sy = 0.0, x, y, d; if (showDebug || (debugLevel > 1)) System.out.println( "correlationMaximum: dist =" + dist + " decimate= " + decimate + " interpSize= " + interpSize); for (i = 0; i < clusterMap.length; i++) if (clusterMap[i] > 0) { x = (i % interpSize - dist * decimate); y = (i / interpSize - dist * decimate); d = interpCorr[i] - limit; s += d; sx += x * d; sy += y * d; } double[] corrXY = { sx / s / decimate + ixc - size / 2, sy / s / decimate + iyc - size / 2 }; if (showDebug || (debugLevel > 1)) System.out.println("correlationMaximum: s =" + s + " sx= " + sx + " sy= " + sy); if (showDebug || (debugLevel > 1)) System.out.println("correlationMaximum: sx/s/decimate =" + (sx / s / decimate) + " sy/s/decimate= " + (sy / s / decimate)); if (showDebug || (debugLevel > 1)) System.out.println("correlationMaximum: dx=" + IJ.d2s(corrXY[0], 3) + " dy=" + IJ.d2s(corrXY[1], 3)); // if ((debugLevel>1) && (showDebug)) { if (showDebug) { double[] decimatedMasked = interpCorr.clone(); for (i = 0; i < decimatedMasked.length; i++) { if (clusterMap[i] == 0) decimatedMasked[i] = limit; } double[][] both = { interpCorr, decimatedMasked }; ShowDoubleFloatArrays.showArrays(both, true, "centerCorr"); } if (decimateFFT > 1) { corrXY[0] /= decimateFFT; corrXY[1] /= decimateFFT; } return corrXY; } private double[] correlationMaximum(double[] corr, double maxOffset, boolean showDebug) { if ((corr == null) || (corr.length == 0)) return null; double[] corrMax = new double[2]; int size = (int) Math.sqrt(corr.length); int i, imax = 0, ix, iy; double max = corr[0]; for (i = 1; i < corr.length; i++) if (max < corr[i]) { max = corr[i]; imax = i; } iy = imax / size; ix = imax % size; corrMax[0] = ix - size / 2; corrMax[1] = iy - size / 2; if ((maxOffset > 0) && (maxOffset * maxOffset < (corrMax[0] * corrMax[0] + corrMax[1] * corrMax[1]))) { if (debugLevel > 1) System.out.println("Too far from the center3: corrMax[0]=" + corrMax[0] + " corrMax[1]=" + corrMax[1]); return null; } if ((ix == 0) || (iy == 0) || (ix == (size - 1)) || (iy == (size - 1))) return corrMax; // on the border - no interpolation; double[] maxInHor = new double[3]; // locations of interpolated maximums for each of 3 rows (iy-1, iy, iy+1) double[] maxInVert = new double[3]; // locations of interpolated maximums for each of 3 columns(ix-1, ix, ix+1) if (debugLevel > 2) System.out.println("correlationMaximum: ix=" + ix + " iy=" + iy); for (i = 0; i < 3; i++) { maxInHor[i] = -0.5 + (corr[imax + size * (i - 1)] - corr[imax + size * (i - 1) - 1]) / (2 * corr[imax + size * (i - 1)] - corr[imax + size * (i - 1) - 1] - corr[imax + size * (i - 1) + 1]); maxInVert[i] = -0.5 + (corr[imax + (i - 1)] - corr[imax + (i - 1) - size]) / (2 * corr[imax + (i - 1)] - corr[imax + (i - 1) - size] - corr[imax + (i - 1) + size]); } if (debugLevel > 2) System.out.println("correlationMaximum: maxInHor[0] =" + maxInHor[0] + " maxInHor[1]= " + maxInHor[1] + " maxInHor[2]= " + maxInHor[2]); if (debugLevel > 2) System.out.println("correlationMaximum: maxInVert[0]=" + maxInVert[0] + " maxInVert[1]=" + maxInVert[1] + " maxInVert[2]=" + maxInVert[2]); int maxInHorIndex = 0; int maxInVertIndex = 0; if ((maxInHor[0] < maxInHor[1]) && (maxInHor[0] < maxInHor[2])) maxInHorIndex = 1; if ((maxInVert[0] < maxInVert[1]) && (maxInVert[0] < maxInVert[2])) maxInVertIndex = 1; if (debugLevel > 2) System.out.println( "correlationMaximum: maxInHorIndex=" + maxInHorIndex + " maxInVertIndex=" + maxInVertIndex); /* * y= (y0+x0(y1-y0))/(1-(x1-x0)(y1-y0)) x= (x0+y0(x1-x0))/(1-(x1-x0)(y1-y0)) d= * (1-(x1-x0)(y1-y0)) y= (y0+x0(y1-y0))/d x= (x0+y0(x1-x0))/d */ double d = 1 - (maxInHor[maxInHorIndex + 1] - maxInHor[maxInHorIndex]) * (maxInVert[maxInVertIndex + 1] - maxInVert[maxInVertIndex]); corrMax[0] = (maxInHor[maxInHorIndex] + maxInVert[maxInVertIndex] * (maxInHor[maxInHorIndex + 1] - maxInHor[maxInHorIndex])) / d; corrMax[1] = (maxInVert[maxInVertIndex] + maxInHor[maxInHorIndex] * (maxInVert[maxInVertIndex + 1] - maxInVert[maxInVertIndex])) / d; if (debugLevel > 2) System.out.println("correlationMaximum: corrMax[0]=" + corrMax[0] + " corrMax[1]=" + corrMax[1]); corrMax[0] += ix - size / 2; corrMax[1] += iy - size / 2; return corrMax; } /* ======================================================================== */ public Rectangle correlationSelection(double[] beforeXY, // initial coordinates of the pattern cross point int size) { int ixc = 2 * ((int) Math.round(beforeXY[0] / 2)); int iyc = 2 * ((int) Math.round(beforeXY[1] / 2)); Rectangle centerCross = new Rectangle(ixc - size, iyc - size, 2 * size, 2 * size); return centerCross; } /* ======================================================================== */ // Estimate center xy and wave vectors from the neigbors // returns {{x,y},{wv1x,wv1y},{wv2x,wv2y}} public double[][] estimateCell(double[][][][] grid, int[] uv0, double[][] weights, // quadrant of sample weights boolean useContrast, // do not use cells with undefined contrast boolean forceLinear, // use linear approximation (instead of quadratic) double thresholdLin, // thershold ratio of matrix determinant to norm for linear approximation (det // too low - fail) double thresholdQuad // thershold ratio of matrix determinant to norm for quadratic approximation // (det too low - fail) ) { int dist = weights.length - 1; int size = dist * 2 + 1; double[][][] samples0 = new double[size * size][3][]; int index = 0; int[] uv = new int[2]; double w; int maxU = -dist - 1, minU = dist + 1, maxV = -dist - 1, minV = dist + 1, maxUpV = -2 * dist - 1, minUpV = 2 * dist + 1, maxUmV = -2 * dist - 1, minUmV = 2 * dist + 1; for (int iDv = -dist; iDv <= dist; iDv++) for (int iDu = -dist; iDu <= dist; iDu++) { uv[0] = uv0[0] + iDu; uv[1] = uv0[1] + iDv; if ((!useContrast && isCellDefined(grid, uv)) || isCellDefinedC(grid, uv)) { w = weights[(iDv >= 0) ? iDv : -iDv][(iDu >= 0) ? iDu : -iDu]; if (w != 0.0) { if (maxU < iDu) maxU = iDu; if (minU > iDu) minU = iDu; if (maxV < iDv) maxV = iDv; if (minV > iDv) minV = iDv; if (maxUpV < (iDu + iDv)) maxUpV = iDu + iDv; if (minUpV > (iDu + iDv)) minUpV = iDu + iDv; if (maxUmV < (iDu - iDv)) maxUmV = iDu - iDv; if (minUmV > (iDu - iDv)) minUmV = iDu - iDv; samples0[index][0] = new double[2]; samples0[index][1] = new double[useContrast ? 3 : 2]; samples0[index][2] = new double[1]; samples0[index][2][0] = w; samples0[index][0][0] = iDu; samples0[index][0][1] = iDv; samples0[index][1][0] = grid[uv[1]][uv[0]][0][0]; samples0[index][1][1] = grid[uv[1]][uv[0]][0][1]; if (useContrast) { samples0[index][1][2] = grid[uv[1]][uv[0]][0][2]; // contrast } index++; } } } if (debugLevel > 3) System.out.println(" maxU-minU=" + (maxU - minU) + " maxV-minV=" + (maxV - minV)); if (debugLevel > 3) System.out.println(" maxUpV-minUpV=" + (maxUpV - minUpV) + " maxUmV-minUmV=" + (maxUmV - minUmV)); int diameter = maxU - minU; if (diameter > (maxV - minV)) diameter = maxV - minV; diameter *= 2; if (diameter > (maxUpV - minUpV)) diameter = (maxUpV - minUpV); if (diameter > (maxUmV - minUmV)) diameter = (maxUmV - minUmV); if (debugLevel > 3) System.out.println(" diameter=" + diameter + " number=" + index); if (diameter < 2) return null; double[][][] samples = new double[index][][]; System.arraycopy(samples0, 0, samples, 0, index); double[][] estimatedCell = interpolateQuadraticWithWvAtZero(samples, // see quadraticApproximation() forceLinear || (diameter < 5), // use linear approximation diameter <4 should be enough, 5 - just to be // safe thresholdLin, // thershold ratio of matrix determinant to norm for linear approximation (det // too low - fail) thresholdQuad); // thershold ratio of matrix determinant to norm for quadratic approximation // (det too low - fail) if ((estimatedCell == null) || (estimatedCell[0] == null) || useContrast) return estimatedCell; double contrast = Double.NaN; if (isCellDefined(grid, uv0)) { double[] xycOld = grid[uv0[1]][uv0[0]][0]; if (xycOld.length > 2) contrast = xycOld[2]; } double[] xyc = { estimatedCell[0][0], estimatedCell[0][1], contrast }; estimatedCell[0] = xyc; return estimatedCell; } public double[] interpolateQuadratic(double[] xy, // coordinates for which the interpolation is needed double[][][] data, // see quadraticApproximation() boolean forceLinear, // use linear approximation double thresholdLin, // thershold ratio of matrix determinant to norm for linear approximation (det // too low - fail) double thresholdQuad) { // thershold ratio of matrix determinant to norm for quadratic approximation // (det too low - fail) double[][] coeff = new PolynomialApproximation(this.debugLevel).quadraticApproximation(data, forceLinear, // use // linear // approximation thresholdLin, // thershold ratio of matrix determinant to norm for linear approximation (det // too low - fail) thresholdQuad); if (coeff == null) return null; double[] result = new double[coeff.length]; int offset = (coeff[0].length > 3) ? 3 : 0; for (int i = 0; i < coeff.length; i++) { result[i] = coeff[i][offset + 0] * xy[0] + coeff[i][offset + 1] * xy[1] + coeff[i][offset + 2]; if (offset > 0) result[i] += coeff[i][0] * xy[0] * xy[0] + coeff[i][1] * xy[1] * xy[1] + coeff[i][2] * xy[0] * xy[1]; } return result; } // returns {{x,y},{wv1x,wv1y},{wv2x,wv2y}} public double[][] interpolateQuadraticWithWvAtZero(double[][][] data, // see quadraticApproximation() boolean forceLinear, // use linear approximation double thresholdLin, // thershold ratio of matrix determinant to norm for linear approximation (det // too low - fail) double thresholdQuad) { // thershold ratio of matrix determinant to norm for quadratic approximation // (det too low - fail) double[][] coeff = new PolynomialApproximation(this.debugLevel).quadraticApproximation(data, forceLinear, // use // linear // approximation thresholdLin, // thershold ratio of matrix determinant to norm for linear approximation (det // too low - fail) thresholdQuad); if (coeff == null) return null; if (coeff.length != 2) return null; double[][] result = new double[3][2]; double[][] uv2xy = new double[2][2]; int offset = (coeff[0].length > 3) ? 3 : 0; for (int i = 0; i < 2; i++) { result[0][i] = coeff[i][offset + 2]; uv2xy[0][i] = coeff[0][offset + i]; uv2xy[1][i] = coeff[1][offset + i]; } double[][] wv = matrix2x2_invert(matrix2x2_scale(uv2xy, 2.0)); for (int i = 0; i < 2; i++) { result[1][i] = wv[0][i]; result[2][i] = wv[1][i]; } if ((debugLevel > 2) && forceLinear) { System.out.println( "*************** interpolateQuadraticWithWvAtZero() linear forced, data.length=" + data.length); } if (debugLevel > 3) { for (int i = 0; i < data.length; i++) { System.out.println(i + ": uv=[" + IJ.d2s(data[i][0][0], 3) + ":" + IJ.d2s(data[i][0][1], 3) + "]" + " xy=[" + IJ.d2s(data[i][1][0], 3) + ":" + IJ.d2s(data[i][1][1], 3) + "]" + " weight=" + IJ.d2s(data[i][2][0], 3)); } String dbgStr = ""; dbgStr += " [[" + IJ.d2s(coeff[0][0], 5) + "/" + IJ.d2s(coeff[0][1], 5) + "/" + IJ.d2s(coeff[0][2], 5); if (coeff[0].length > 3) dbgStr += "/" + IJ.d2s(coeff[0][3], 5) + "/" + IJ.d2s(coeff[0][4], 5) + "/" + IJ.d2s(coeff[0][5], 5) + "]]"; dbgStr += " [[" + IJ.d2s(coeff[1][0], 5) + "/" + IJ.d2s(coeff[1][1], 5) + "/" + IJ.d2s(coeff[1][2], 5); if (coeff[1].length > 3) dbgStr += "/" + IJ.d2s(coeff[1][3], 5) + "/" + IJ.d2s(coeff[1][4], 5) + "/" + IJ.d2s(coeff[1][5], 5) + "]]"; System.out.println(dbgStr); for (int i = 0; i < 2; i++) { System.out.println(i + ": uv2xy=" + IJ.d2s(uv2xy[i][0], 3) + ":" + IJ.d2s(uv2xy[i][1], 3)); } for (int i = 0; i < 2; i++) { System.out.println(i + ": wv=" + IJ.d2s(wv[i][0], 3) + ":" + IJ.d2s(wv[i][1], 3)); } } return result; } // calculate simulation parameters for quadratic distortion of the pattern, // compatible with SimulationPattern class // Returns 2 lines {{wv1x, wv1y, u0, Ax, Bx, Cx},{wv2x, wv2y, v0, Ay, By, Cy}} // if quadratic is not possible, only {{wv1x, wv1y, u0},{wv2x, wv2y}} will be // returned // or just null if even linear is not possible // data array consists of lines of either 2 or 3 vectors: // 2-element vector x,y // 2 element vector u,v // optional 1- element vector w (weight of the sample) public double[][] getSimulationParametersFromGrid(double[][][][] grid, int[] uv0, // U,V of the center point (for // which the simulation pattern // should be built double[] xy0, // x,y of the center point (or null to use grid) double[][] weights, // quadrant of sample weights boolean forceLinear, // use linear approximation (instead of quadratic) double thresholdLin, // thershold ratio of matrix determinant to norm for linear approximation (det // too low - fail) double thresholdQuad // thershold ratio of matrix determinant to norm for quadratic approximation // (det too low - fail) ) { int dist = weights.length - 1; int size = dist * 2 + 1; double[][][] samples0 = new double[size * size][3][]; int index = 0; int[] uv = new int[2]; double w; if (xy0 == null) { if (isCellDefined(grid, uv0)) { xy0 = new double[2]; xy0[0] = grid[uv0[1]][uv0[0]][0][0]; xy0[1] = grid[uv0[1]][uv0[0]][0][1]; } else { return null; // xy of the center is not known } } int maxU = -dist - 1, minU = dist + 1, maxV = -dist - 1, minV = dist + 1, maxUpV = -2 * dist - 1, minUpV = 2 * dist + 1, maxUmV = -2 * dist - 1, minUmV = 2 * dist + 1; for (int iDv = -dist; iDv <= dist; iDv++) for (int iDu = -dist; iDu <= dist; iDu++) { uv[0] = uv0[0] + iDu; uv[1] = uv0[1] + iDv; if ((uv[0] >= 0) && (uv[1] >= 0) && (uv[1] < grid.length) && (uv[0] < grid[uv[1]].length) && (isCellDefined(grid, uv))) { w = weights[(iDv >= 0) ? iDv : -iDv][(iDu >= 0) ? iDu : -iDu]; if (w != 0.0) { if (maxU < iDu) maxU = iDu; if (minU > iDu) minU = iDu; if (maxV < iDv) maxV = iDv; if (minV > iDv) minV = iDv; if (maxUpV < (iDu + iDv)) maxUpV = iDu + iDv; if (minUpV > (iDu + iDv)) minUpV = iDu + iDv; if (maxUmV < (iDu - iDv)) maxUmV = iDu - iDv; if (minUmV > (iDu - iDv)) minUmV = iDu - iDv; samples0[index][0] = new double[2]; samples0[index][1] = new double[2]; samples0[index][2] = new double[1]; samples0[index][2][0] = w; samples0[index][0][0] = grid[uv[1]][uv[0]][0][0] - xy0[0]; samples0[index][0][1] = grid[uv[1]][uv[0]][0][1] - xy0[1]; samples0[index][1][0] = uv[0]; samples0[index][1][1] = uv[1]; if (debugLevel > 20) { System.out.println("iDu=" + iDu + " iDv=" + iDv + " " + " uv[0]=" + IJ.d2s(uv[0], 3) + " uv[1]=" + IJ.d2s(uv[1], 3) + " " + " samples0[" + index + "][0][0]=" + IJ.d2s(samples0[index][0][0], 3) + " samples0[" + index + "][0][1]=" + IJ.d2s(samples0[index][0][1], 3) + " " + " samples0[" + index + "][1][0]=" + IJ.d2s(samples0[index][1][0], 3) + " samples0[" + index + "][1][1]=" + IJ.d2s(samples0[index][1][1], 3)); } index++; } } } int diameter = maxU - minU; if (diameter > (maxV - minV)) diameter = maxV - minV; diameter *= 2; if (diameter > (maxUpV - minUpV)) diameter = (maxUpV - minUpV); if (diameter > (maxUmV - minUmV)) diameter = (maxUmV - minUmV); if (debugLevel > 2) System.out.println(" diameter=" + diameter + " number=" + index); if (diameter < 2) return null; double[][][] samples = new double[index][][]; System.arraycopy(samples0, 0, samples, 0, index); double[][] simulParams = getSimulationParametersFromSamples(samples, // see quadraticApproximation() forceLinear || (diameter < 5), // use linear approximation diameter <4 should be enough, 5 - just to be // safe thresholdLin, // thershold ratio of matrix determinant to norm for linear approximation (det // too low - fail) thresholdQuad); // thershold ratio of matrix determinant to norm for quadratic approximation // (det too low - fail) return simulParams; } public double[][] getSimulationParametersFromSamples(double[][][] data, // see quadraticApproximation() boolean forceLinear, // use linear approximation double thresholdLin, // thershold ratio of matrix determinant to norm for linear approximation (det // too low - fail) double thresholdQuad) { // thershold ratio of matrix determinant to norm for quadratic approximation // (det too low - fail) double[][] coeff = new PolynomialApproximation(this.debugLevel).quadraticApproximation(data, forceLinear, // use // linear // approximation thresholdLin, // thershold ratio of matrix determinant to norm for linear approximation (det // too low - fail) thresholdQuad); if (debugLevel > 2) { for (int i = 0; i < data.length; i++) { System.out.println(i + ": xy=[" + IJ.d2s(data[i][0][0], 3) + ":" + IJ.d2s(data[i][0][1], 3) + "]" + " uv=[" + IJ.d2s(data[i][1][0], 3) + ":" + IJ.d2s(data[i][1][1], 3) + "]" + " weight=" + IJ.d2s(data[i][2][0], 3)); } String dbgStr = ""; dbgStr += " [" + IJ.d2s(coeff[0][0], 5) + "/" + IJ.d2s(coeff[0][1], 5) + "/" + IJ.d2s(coeff[0][2], 5); if (coeff[0].length > 3) dbgStr += "/" + IJ.d2s(coeff[0][3], 5) + "/" + IJ.d2s(coeff[0][4], 5) + "/" + IJ.d2s(coeff[0][5], 5) + "]"; dbgStr += " [" + IJ.d2s(coeff[1][0], 5) + "/" + IJ.d2s(coeff[1][1], 5) + "/" + IJ.d2s(coeff[1][2], 5); if (coeff[1].length > 3) dbgStr += "/" + IJ.d2s(coeff[1][3], 5) + "/" + IJ.d2s(coeff[1][4], 5) + "/" + IJ.d2s(coeff[1][5], 5) + "]"; System.out.println(dbgStr); } if (coeff == null) return null; if (coeff.length != 2) return null; boolean isQuad = coeff[0].length > 3; int offset = isQuad ? 3 : 0; double[][] result = new double[2][isQuad ? 6 : 3]; double[][] xy2uv = new double[2][2]; for (int i = 0; i < 2; i++) { result[i][2] = coeff[i][offset + 2]; // F xy2uv[i][0] = coeff[i][offset + 0]; // D xy2uv[i][1] = coeff[i][offset + 1]; // E result[i][0] = 0.5 * xy2uv[i][0]; // 0.5 because uv grid is 0.5 (pos/neg) result[i][1] = 0.5 * xy2uv[i][1]; // } if (isQuad) { double[][] uv2xy = matrix2x2_invert(xy2uv); // double [][] ABCuv={{coeff[0][0],coeff[0][1],0.5*coeff[0][2]}, // {coeff[1][0],coeff[1][1],0.5*coeff[1][2]}}; double[][] ABCuv = { { 4 * coeff[0][0], 4 * coeff[0][1], 2 * coeff[0][2] }, // correction that uv grid is // 0.5 { 4 * coeff[1][0], 4 * coeff[1][1], 2 * coeff[1][2] } }; double[][] ABCxy = new double[2][3]; for (int i = 0; i < 2; i++) for (int j = 0; j < 3; j++) { ABCxy[i][j] = 0.0; for (int k = 0; k < 2; k++) ABCxy[i][j] += uv2xy[i][k] * ABCuv[k][j]; } for (int i = 0; i < 2; i++) for (int j = 0; j < 3; j++) result[i][j + 3] = ABCxy[i][j]; } return result; } public double[][] findPatternFromGrid(int x0, // top-left pixel of the square WOI int y0, int size, // size of square (pixels) double[] halfWindow, boolean forceLinear, // use linear approximation (instead of quadratic) double thresholdLin, // thershold ratio of matrix determinant to norm for linear approximation (det // too low - fail) double thresholdQuad // thershold ratio of matrix determinant to norm for quadratic approximation // (det too low - fail) ) { // only half-window - half height by half width if (this.PATTERN_GRID == null) { String msg = "PATTERN_GRID is needed, but undefined"; IJ.showMessage("Error", msg); throw new IllegalArgumentException(msg); } if (this.PATTERN_GRID.length == 0) return null; double x1 = x0, y1 = y0; double x2 = x1 + size; double y2 = y1 + size; double x, y; List<Integer> nodeList = new ArrayList<Integer>(1000); Integer Index; int len = this.PATTERN_GRID[0].length; for (int v = 0; v < this.PATTERN_GRID.length; v++) for (int u = 0; u < len; u++) if ((this.PATTERN_GRID[v][u] != null) && (this.PATTERN_GRID[v][u][0] != null)) { x = this.PATTERN_GRID[v][u][0][0]; y = this.PATTERN_GRID[v][u][0][1]; if ((x >= x1) && (x < x2) && (y >= y1) && (y < y2)) { Index = v * len + u; nodeList.add(Index); } } double[][][] samples = new double[nodeList.size()][3][]; // pattern parameters are referenced to the center of the square double xc = x0 + size / 2; double yc = y0 + size / 2; int halfSize = size / 2; for (int i = 0; i < samples.length; i++) { int uv = nodeList.get(i); int v = uv / len; int u = uv % len; samples[i][0] = new double[2]; samples[i][0][0] = this.PATTERN_GRID[v][u][0][0] - xc; samples[i][0][1] = this.PATTERN_GRID[v][u][0][1] - yc; samples[i][1] = new double[2]; samples[i][1][0] = u; samples[i][1][1] = v; samples[i][2] = new double[1]; int iy = ((int) Math.round((this.PATTERN_GRID[v][u][0][1] - y1) / 2)); int ix = ((int) Math.round((this.PATTERN_GRID[v][u][0][0] - x1) / 2)); samples[i][2][0] = ((iy >= 0) && (iy < halfSize) && (ix >= 0) && (ix < halfSize)) ? halfWindow[iy * halfSize + ix] : 0.0; } double[][] simulParams = getSimulationParametersFromSamples(samples, // see quadraticApproximation() forceLinear || (halfSize < 5), // use linear approximation diameter <4 should be enough, 5 - just to be // safe thresholdLin, // thershold ratio of matrix determinant to norm for linear approximation (det // too low - fail) thresholdQuad); // thershold ratio of matrix determinant to norm for quadratic approximation // (det too low - fail) return simulParams; } /** Moved to PolyninomialApproximation class */ /** * 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 arrrray 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 [][] quadraticApproximation( double [][][] data, boolean * forceLinear, // use linear approximation double thresholdLin, // thershold * ratio of matrix determinant to norm for linear approximation (det too low - * fail) double thresholdQuad // thershold ratio of matrix determinant to norm * for quadratic approximation (det too low - fail) ){ /* ix, iy - the location * of the point with maximal value. We'll approximate the vicinity of that * maximum using a second degree polynominal: * Z(x,y)~=A*x^2+B*y^2+C*x*y+D*x+E*y+F by minimizing sum of squared * differenceS00between the actual (Z(x,uy)) and approximated values. and then * find the maximum on the approximated surface. Here iS00the math: * * Z(x,y)~=A*x^2+B*y^2+C*x*y+D*x+E*y+F minimizing squared error, using W(x,y) * aS00weight function * * error=Sum(W(x,y)*((A*x^2+B*y^2+C*x*y+D*x+E*y+F)-Z(x,y))^2) * * error=Sum(W(x,y)*(A^2*x^4 + 2*A*x^2*(B*y^2+C*x*y+D*x+E*y+F-Z(x,y)) +(...) ) * 0=derror/dA=Sum(W(x,y)*(2*A*x^4 + 2*x^2*(B*y^2+C*x*y+D*x+E*y+F-Z(x,y))) * 0=Sum(W(x,y)*(A*x^4 + x^2*(B*y^2+C*x*y+D*x+E*y+F-Z(x,y))) * * S40=Sum(W(x,y)*x^4), etc * * (1) 0=A*S40 + B*S22 + C*S31 +D*S30 +E*S21 +F*S20 - SZ20 * * derror/dB: * * error=Sum(W(x,y)*(B^2*y^4 + 2*B*y^2*(A*x^2+C*x*y+D*x+E*y+F-Z(x,y)) +(...) ) * 0=derror/dB=Sum(W(x,y)*(2*B*y^4 + 2*y^2*(A*x^2+C*x*y+D*x+E*y+F-Z(x,y))) * 0=Sum(W(x,y)*(B*y^4 + y^2*(A*x^2+C*x*y+D*x+E*y+F-Z(x,y))) * * (2) 0=B*S04 + A*S22 + C*S13 +D*S12 +E*S03 +F*SY2 - SZ02 (2) 0=A*S22 + B*S04 + * C*S13 +D*S12 +E*S03 +F*SY2 - SZ02 * * derror/dC: * * error=Sum(W(x,y)*(C^2*x^2*y^2 + 2*C*x*y*(A*x^2+B*y^2+D*x+E*y+F-Z(x,y)) +(...) * ) 0=derror/dC=Sum(W(x,y)*(2*C*x^2*y^2 + 2*x*y*(A*x^2+B*y^2+D*x+E*y+F-Z(x,y)) * ) 0=Sum(W(x,y)*(C*x^2*y^2 + x*y*(A*x^2+B*y^2+D*x+E*y+F-Z(x,y)) ) * * (3) 0= A*S31 + B*S13 + C*S22 + D*S21 + E*S12 + F*S11 - SZ11 * * derror/dD: * * error=Sum(W(x,y)*(D^2*x^2 + 2*D*x*(A*x^2+B*y^2+C*x*y+E*y+F-Z(x,y)) +(...) ) * 0=derror/dD=Sum(W(x,y)*(2*D*x^2 + 2*x*(A*x^2+B*y^2+C*x*y+E*y+F-Z(x,y)) ) * 0=Sum(W(x,y)*(D*x^2 + x*(A*x^2+B*y^2+C*x*y+E*y+F-Z(x,y)) ) * * (4) 0= A*S30 + B*S12 + C*S21 + D*S20 + E*S11 + F*S10 - SZ10 * * derror/dE: * * error=Sum(W(x,y)*(E^2*y^2 + 2*E*y*(A*x^2+B*y^2+C*x*y+D*x+F-Z(x,y)) +(...) ) * 0=derror/dE=Sum(W(x,y)*(2*E*y^2 + 2*y*(A*x^2+B*y^2+C*x*y+D*x+F-Z(x,y)) ) * 0=Sum(W(x,y)*(E*y^2 + y*(A*x^2+B*y^2+C*x*y+D*x+F-Z(x,y)) ) (5) 0= A*S21 + * B*S03 + C*S12 + D*S11 + E*SY2 + F*SY - SZ01 * * derror/dF: * * error=Sum(W(x,y)*(F^2 + 2*F*(A*x^2+B*y^2+C*x*y+D*x+E*y-Z(x,y)) +(...) ) * 0=derror/dF=Sum(W(x,y)*(2*F + 2*(A*x^2+B*y^2+C*x*y+D*x+E*y-Z(x,y)) ) * 0=Sum(W(x,y)*(F + (A*x^2+B*y^2+C*x*y+D*x+E*y-Z(x,y)) ) (6) 0= A*S20 + B*SY2 + * C*S11 + D*S10 + E*SY + F*S00 - SZ00 * * * (1) 0= A*S40 + B*S22 + C*S31 + D*S30 + E*S21 + F*S20 - SZ20 (2) 0= A*S22 + * B*S04 + C*S13 + D*S12 + E*S03 + F*S02 - SZ02 (3) 0= A*S31 + B*S13 + C*S22 + * D*S21 + E*S12 + F*S11 - SZ11 (4) 0= A*S30 + B*S12 + C*S21 + D*S20 + E*S11 + * F*S10 - SZ10 (5) 0= A*S21 + B*S03 + C*S12 + D*S11 + E*S02 + F*S01 - SZ01 (6) * 0= A*S20 + B*S02 + C*S11 + D*S10 + E*S01 + F*S00 - SZ00 / int * zDim=data[0][1].length; * * double w,z,x,x2,x3,x4,y,y2,y3,y4,wz; int i,j,n=0; double S00=0.0, * S10=0.0,S01=0.0, S20=0.0,S11=0.0,S02=0.0, S30=0.0,S21=0.0,S12=0.0,S03=0.0, * S40=0.0,S31=0.0,S22=0.0,S13=0.0,S04=0.0; double [] SZ00=new double [zDim]; * double [] SZ01=new double [zDim]; double [] SZ10=new double [zDim]; double [] * SZ11=new double [zDim]; double [] SZ02=new double [zDim]; double [] SZ20=new * double [zDim]; for (i=0;i<zDim;i++) { SZ00[i]=0.0; SZ01[i]=0.0; SZ10[i]=0.0; * SZ11[i]=0.0; SZ02[i]=0.0; SZ20[i]=0.0; } for (i=0;i<data.length;i++) { * w=(data[i].length>2)? data[i][2][0]:1.0; if (w>0) { n++; x=data[i][0][0]; * y=data[i][0][1]; x2=x*x; y2=y*y; S00+=w; S10+=w*x; S01+=w*y; S11+=w*x*y; * S20+=w*x2; S02+=w*y2; if (!forceLinear) { x3=x2*x; x4=x3*x; y3=y2*y; y4=y3*y; * S30+=w*x3; S21+=w*x2*y; S12+=w*x*y2; S03+=w*y3; S40+=w*x4; S31+=w*x3*y; * S22+=w*x2*y2; S13+=w*x*y3; S04+=w*y4; } for (j=0;j<zDim;j++) { * z=data[i][1][j]; wz=w*z; SZ00[j]+=wz; SZ10[j]+=wz*x; SZ01[j]+=wz*y; if * (!forceLinear) { SZ20[j]+=wz*x2; SZ11[j]+=wz*x*y; SZ02[j]+=wz*y2; } } * * } } //need to decide if there is enough data for linear and quadratic double * [][] mAarrayL= { {S20,S11,S10}, {S11,S02,S01}, {S10,S01,S00}}; Matrix M=new * Matrix (mAarrayL); Matrix Z; if (debugLevel>3) * System.out.println(">>> n="+n+" det_lin="+M.det()+" norm_lin="+normMatix( * mAarrayL)); double nmL=normMatix(mAarrayL); if ((nmL==0.0) || * (Math.abs(M.det())/nmL<thresholdLin)) return null; // not enough data even * for the linear approximation double []zAarrayL=new double [3]; double [][] * ABCDEF=new double[zDim][]; // double [] zAarrayL={SZ10,SZ01,SZ00}; for * (i=0;i<zDim;i++) { zAarrayL[0]=SZ10[i]; zAarrayL[1]=SZ01[i]; * zAarrayL[2]=SZ00[i]; Z=new Matrix (zAarrayL,3); ABCDEF[i]= * M.solve(Z).getRowPackedCopy(); } if (forceLinear) return ABCDEF; // quote try * quadratic approximation double [][] mAarrayQ= { {S40,S22,S31,S30,S21,S20}, * {S22,S04,S13,S12,S03,S02}, {S31,S13,S22,S21,S12,S11}, * {S30,S12,S21,S20,S11,S10}, {S21,S03,S12,S11,S02,S01}, * {S20,S02,S11,S10,S01,S00}}; M=new Matrix (mAarrayQ); if (debugLevel>3) * System.out.println(" n="+n+" det_quad="+M.det()+" norm_quad="+normMatix( * mAarrayQ)+" data.length="+data.length); double nmQ=normMatix(mAarrayQ); if * ((nmQ==0.0) || (Math.abs(M.det())/normMatix(mAarrayQ)<thresholdQuad)) { * System.out.println("Using linear approximation, M.det()="+M.det() * +" normMatix(mAarrayQ)="+normMatix(mAarrayQ)); //did not happen return * ABCDEF; // not enough data for the quadratic approximation, return linear } * // double [] zAarrayQ={SZ20,SZ02,SZ11,SZ10,SZ01,SZ00}; double [] zAarrayQ=new * double [6]; for (i=0;i<zDim;i++) { zAarrayQ[0]=SZ20[i]; zAarrayQ[1]=SZ02[i]; * zAarrayQ[2]=SZ11[i]; zAarrayQ[3]=SZ10[i]; zAarrayQ[4]=SZ01[i]; * zAarrayQ[5]=SZ00[i]; Z=new Matrix (zAarrayQ,6); ABCDEF[i]= * M.solve(Z).getRowPackedCopy(); } return ABCDEF; } // calcualte "volume" made * of the matrix row-vectors, placed orthogonally // to be compared to * determinant public double normMatix(double [][] a) { double d,norm=1.0; for * (int i=0;i<a.length;i++) { d=0; for (int j=0;j<a[i].length;j++) * d+=a[i][j]*a[i][j]; norm*=Math.sqrt(d); } return norm; } */ /* ======================================================================== */ public double[][][][] setPatternGridArray(int size) { return setPatternGridArray(size, size); } public double[][][][] setPatternGridArray(int width, int height) { int i, j; double[][][][] result = new double[height][width][][]; for (i = 0; i < height; i++) for (j = 0; j < width; j++) result[i][j] = null; return result; } public static class PatternDetectParameters { public double gaussWidth; // <=0 - use Hamming window public double corrGamma; public double corrSigma; public int diffSpectrCorr; public double shrinkClusters; public int multiplesToTry; public double deviation; public int deviationSteps; public double highpass; public double corrRingWidth; public double minCorrContrast; public double minGridPeriod; public double maxGridPeriod; public double minGridPeriodLwir; public int minGridFileSize = 15000; // Minimal file size (to overwrite) public double maxGridPeriodLwir; public double debugX; public double debugY; public double debugRadius; // added for large cell findPattern (based on phase correlation) public boolean use_large_cells = false; // new method based on phase correlation should work with large cells, // so only first negative correlation (1/2 period) fits in window public double phaseCoeff = 0.5; // "phasiness" of correlation public double lowpass_sigma = .3; // for phase correlation - frequency fraction of maximal public double min_frac = 0.03; // do not use higher order autocorrelation if min/max // is weaker than this fraction of the zero maximum public double min_sin = 0.5; // minimal sine for the angle between two pattern vectors public boolean no_crazy = true; // fail if quadratic approximation fails or returns outside of +/- 1.5 public PatternDetectParameters(double gaussWidth, double corrGamma, double corrSigma, int diffSpectrCorr, double shrinkClusters, int multiplesToTry, double deviation, int deviationSteps, double highpass, double corrRingWidth, double minCorrContrast, double minGridPeriod, double maxGridPeriod, double minGridPeriodLwir, double maxGridPeriodLwir, double debugX, double debugY, double debugRadius, boolean use_large_cells, double phaseCoeff, double lowpass_sigma, double min_frac, double min_sin, boolean no_crazy) { this.gaussWidth = gaussWidth; this.corrGamma = corrGamma; this.corrSigma = corrSigma; this.diffSpectrCorr = diffSpectrCorr; this.shrinkClusters = shrinkClusters; this.multiplesToTry = multiplesToTry; this.deviation = deviation; this.deviationSteps = deviationSteps; this.highpass = highpass; this.corrRingWidth = corrRingWidth; this.minCorrContrast = minCorrContrast; this.minGridPeriod = minGridPeriod; this.maxGridPeriod = maxGridPeriod; this.minGridPeriodLwir = minGridPeriodLwir; this.maxGridPeriodLwir = maxGridPeriodLwir; this.debugX = debugX; this.debugY = debugY; this.debugRadius = debugRadius; this.use_large_cells = use_large_cells; this.phaseCoeff = phaseCoeff; this.lowpass_sigma = lowpass_sigma; this.min_frac = min_frac; this.min_sin = min_sin; this.no_crazy = no_crazy; } public void setProperties(String prefix, Properties properties) { properties.setProperty(prefix + "gaussWidth", this.gaussWidth + ""); properties.setProperty(prefix + "corrGamma", this.corrGamma + ""); properties.setProperty(prefix + "corrSigma", this.corrSigma + ""); properties.setProperty(prefix + "diffSpectrCorr", this.diffSpectrCorr + ""); properties.setProperty(prefix + "shrinkClusters", this.shrinkClusters + ""); properties.setProperty(prefix + "multiplesToTry", this.multiplesToTry + ""); properties.setProperty(prefix + "deviation", this.deviation + ""); properties.setProperty(prefix + "deviationSteps", this.deviationSteps + ""); properties.setProperty(prefix + "highpass", this.highpass + ""); properties.setProperty(prefix + "corrRingWidth", this.corrRingWidth + ""); properties.setProperty(prefix + "minCorrContrast", this.minCorrContrast + ""); properties.setProperty(prefix + "minGridPeriod", this.minGridPeriod + ""); properties.setProperty(prefix + "maxGridPeriod", this.maxGridPeriod + ""); properties.setProperty(prefix + "minGridPeriodLwir", this.minGridPeriodLwir + ""); properties.setProperty(prefix + "maxGridPeriodLwir", this.maxGridPeriodLwir + ""); properties.setProperty(prefix + "debugX", this.debugX + ""); properties.setProperty(prefix + "debugY", this.debugY + ""); properties.setProperty(prefix + "debugRadius", this.debugRadius + ""); properties.setProperty(prefix + "use_large_cells", this.use_large_cells + ""); properties.setProperty(prefix + "phaseCoeff", this.phaseCoeff + ""); properties.setProperty(prefix + "lowpass_sigma", this.lowpass_sigma + ""); properties.setProperty(prefix + "min_frac", this.min_frac + ""); properties.setProperty(prefix + "min_sin", this.min_sin + ""); properties.setProperty(prefix + "no_crazy", this.no_crazy + ""); } public void getProperties(String prefix, Properties properties) { this.gaussWidth = Double.parseDouble(properties.getProperty(prefix + "gaussWidth")); this.corrGamma = Double.parseDouble(properties.getProperty(prefix + "corrGamma")); this.corrSigma = Double.parseDouble(properties.getProperty(prefix + "corrSigma")); this.diffSpectrCorr = Integer.parseInt(properties.getProperty(prefix + "diffSpectrCorr")); this.shrinkClusters = Double.parseDouble(properties.getProperty(prefix + "shrinkClusters")); this.multiplesToTry = Integer.parseInt(properties.getProperty(prefix + "multiplesToTry")); this.deviation = Double.parseDouble(properties.getProperty(prefix + "deviation")); this.deviationSteps = Integer.parseInt(properties.getProperty(prefix + "deviationSteps")); this.highpass = Double.parseDouble(properties.getProperty(prefix + "highpass")); this.corrRingWidth = Double.parseDouble(properties.getProperty(prefix + "corrRingWidth")); this.minCorrContrast = Double.parseDouble(properties.getProperty(prefix + "minCorrContrast")); if (properties.getProperty(prefix + "minGridPeriod") != null) this.minGridPeriod = Double.parseDouble(properties.getProperty(prefix + "minGridPeriod")); else this.minGridPeriod = 0.0; if (properties.getProperty(prefix + "maxGridPeriod") != null) this.minGridPeriod = Double.parseDouble(properties.getProperty(prefix + "maxGridPeriod")); else this.maxGridPeriod = 0.0; if (properties.getProperty(prefix + "minGridPeriodLwir") != null) this.minGridPeriodLwir = Double.parseDouble(properties.getProperty(prefix + "minGridPeriodLwir")); else this.minGridPeriodLwir = 0.0; if (properties.getProperty(prefix + "maxGridPeriodLwir") != null) this.minGridPeriodLwir = Double.parseDouble(properties.getProperty(prefix + "maxGridPeriodLwir")); else this.maxGridPeriodLwir = 0.0; if (properties.getProperty(prefix + "debugX") != null) this.debugX = Double.parseDouble(properties.getProperty(prefix + "debugX")); if (properties.getProperty(prefix + "debugY") != null) this.debugY = Double.parseDouble(properties.getProperty(prefix + "debugY")); if (properties.getProperty(prefix + "debugRadius") != null) this.debugRadius = Double.parseDouble(properties.getProperty(prefix + "debugRadius")); if (properties.getProperty(prefix + "use_large_cells") != null) this.use_large_cells = Boolean.parseBoolean(properties.getProperty(prefix + "use_large_cells")); if (properties.getProperty(prefix + "phaseCoeff") != null) this.phaseCoeff = Double.parseDouble(properties.getProperty(prefix + "phaseCoeff")); if (properties.getProperty(prefix + "lowpass_sigma") != null) this.lowpass_sigma = Double.parseDouble(properties.getProperty(prefix + "lowpass_sigma")); if (properties.getProperty(prefix + "min_frac") != null) this.min_frac = Double.parseDouble(properties.getProperty(prefix + "min_frac")); if (properties.getProperty(prefix + "min_sin") != null) this.min_sin = Double.parseDouble(properties.getProperty(prefix + "min_sin")); if (properties.getProperty(prefix + "no_crazy") != null) this.no_crazy = Boolean.parseBoolean(properties.getProperty(prefix + "no_crazy")); } } /* ======================================================================== */ public static class DistortionParameters { // TODO: make configurable public double threshold_contrast = 10.0; public int threshold_number = 10; // grid should have this number of nodes with above-threshold contrast private int correlationSize; // FFTSize/4 private int correlationSizeLwir; private int maximalCorrelationSize; // FFTSize/2 private int maximalCorrelationSizeLwir; public double correlationGaussWidth; // 0 - no window, <0 - use Hamming public boolean absoluteCorrelationGaussWidth = false; // do not scale correlationGaussWidth when the FFT size is // increased public int zeros; // leave this number of zeros on the margins of the window (toatal from both // sides). If correlationGaussWidth>0 will // additionally multiply by Hamming private int FFTSize; private int FFTSize_lwir; private int FFTOverlap; // 32 used for aberration kernels, former FFT_OVERLAP private int FFTOverlap_lwir; // 4 public double fftGaussWidth; public double phaseCorrelationFraction = 1.0; // 1.0 - phase correlation, 0.0 - just cross-correlation public double correlationHighPassSigma; public double correlationLowPassSigma; public double correlationRingWidth; // ring (around r=0.5 dist to opposite corr) width , center circle // r=0.5*correlationRingWidth public double correlationMaxOffset; // maximal distance between predicted and actual pattern node public double correlationMinContrast; // minimal contrast for the pattern to pass public double correlationMinInitialContrast; // minimal contrast for the pattern of the center (initial point) public double correlationMinAbsoluteContrast; // minimal contrast for the pattern to pass, does not compensate // for low ligt public double correlationMinAbsoluteInitialContrast; // minimal contrast for the pattern of the center (initial // point) public double scaleFirstPassContrast; // Decrease contrast of cells that are too close to the border to be // processed in refinement pass public double contrastSelectSigmaCenter; // Gaussian sigma to select correlation centers in pixels, 2.0 (center // spot) public double contrastSelectSigma; // Gaussian sigma to select correlation centers (fraction of UV period), 0.1 public double contrastAverageSigma; // Gaussian sigma to average correlation variations (as contrast reference) // 0.5 private int minimalPatternCluster; // minimal pattern cluster size (0 - disable retries) private int minimalPatternClusterLwir; // minimal pattern cluster size (0 - disable retries) public double scaleMinimalInitialContrast; // increase/decrease minimal contrast if initial cluster is >0 but // less than minimalPatternCluster public double searchOverlap; // when searching for grid, step this amount of the FFTSize public int patternSubdiv; public double correlationDx; // not saved public double correlationDy; // not saved public int gridSize; public int loop_debug_level; public boolean refineCorrelations; public boolean fastCorrelationOnFirstPass; public boolean fastCorrelationOnFinalPass; public double bPatternSigma; // blur bPattern with this sigma public double barraySigma; // blur barray with this sigma, multiplied by subdiv public double correlationWeightSigma; // sigma (in pixels) for maximum approximation - UNUSED (other maximum // methods) public double correlationRadiusScale; // maximal radius to consider, in sigmas (if 0 - use sigma as radius) - // UNUSED public int correlationRadius; // radius (green pixel) of the correlation maximum to use for x/y measurement public double correlationThreshold; // fraction of the value of the maximum fro the point to be included in // centroid calculation public int correlationSubdiv; // Total subdivision of the correlation maximum (linear and FFT) public int correlationFFTSubdiv; // Increase density of the correlation using FFT public boolean correlationAverageOnRefine; // average position between neighbor samples public boolean refineInPlace; // Update coordinates of the grid points as they are recalculated (false - then // update all at once) public double averageOrthoDist; // distance to up/down/right left neighbors (0.5) public double averageOrthoWeight; // weight of 4 ortho neighbors (combined) - 0.4), weight of center -s // 1.0-averageOrthoWeight-averageDiagWeight public double averageDiagDist; // distance to diagonal neighbors (projection on x/y) (0.5) public double averageDiagWeight; // weight of 4 diagonal neighbors (combined) - 0.4) public boolean useQuadratic; // use quadratic extrapolation to predict position/wave vectors of a new pixel // (false - use linear) public boolean removeLast; // remove outer (unreliable) row of nodes public int numberExtrapolated; // add this number of extrapolated nodes public double extrapolationSigma; // use instead of the correlationWeightSigma during final extrapolation public double minUVSpan; // Minimal u/v span in correlation window that triggers increase of the // correlation FFT size 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 double flatFieldMin = 0.1; // do not try to compensate if intensity less than this part of maximal public double flatFieldShrink = 1.0; // Shrink before extrapolating intensity map (relative to the average grid // period) public double flatFieldExpand = 3.0; // Expand during extrapolation (relative to the average grid period) public double flatFieldSigmaRadius = 1.0;// Extrapolation weight effective radius (relative to the average grid // period) public double flatFieldExtraRadius = 1.5;// Consider pixels in a square with the side twice this (relative to // flatFieldSigmaRadius) public double averagingAreaScale = 2.0; // multiply the average grid period to determine the area for averaging // the grig brightness // match pointers errors public int errTooFewCells = -10; public int errPatternNotFound = -11; public int errRefineFailed = -12; public boolean legacyMode = false; // legacy mode public int getCorrelationSize(int sensor_type) { switch (sensor_type) { case 1: return correlationSizeLwir; default: return correlationSize;} } public void setCorrelationSize(int size, int sensor_type) { switch (sensor_type) { case 1: correlationSizeLwir = size; break; default: correlationSize = size;} } public int getMaximalCorrelationSize(int sensor_type) { switch (sensor_type) { case 1: return maximalCorrelationSizeLwir; default: return maximalCorrelationSize;} } public int getFFTSize(int sensor_type) { switch (sensor_type) { case 1: return FFTSize_lwir; default: return FFTSize;} } /* public void setFFTSize(int size, int sensor_type) { switch (sensor_type) { case 1: FFTSize_lwir = size; break; default: FFTSize = size; } } */ public int getFFTOverlap(int sensor_type) { switch (sensor_type) { case 1: return FFTOverlap_lwir; default: return FFTOverlap;} } public int getMinimalPatternCluster(int sensor_type) { switch (sensor_type) { case 1: return minimalPatternClusterLwir; default: return minimalPatternCluster;} } public void setMinimalPatternCluster(int size, int sensor_type) { switch (sensor_type) { case 1: minimalPatternClusterLwir = size; break; default: minimalPatternCluster = size;} } public boolean showDistortionDialog(int [] mdl) { //MatchSimulatedPattern.DistortionParameters distortionParameters) { MatchSimulatedPattern.DistortionParameters distortionParameters = this; int i; GenericDialog gd = new GenericDialog("Distrortion parameters"); gd.addNumericField("FFTSize (Initial pattern and aberraton kernels):", distortionParameters.FFTSize, 0); // 256 gd.addNumericField("FFTSize for LWIR sensors):", distortionParameters.FFTSize_lwir, 0); // 32 gd.addNumericField("FFTOverlap (aberration kernels):", distortionParameters.FFTOverlap, 0); // 32 gd.addNumericField("FFTOverlap for LWIR sensors):", distortionParameters.FFTOverlap_lwir, 0); // 4 gd.addNumericField("FFT Gaussian width (relative):", distortionParameters.fftGaussWidth, 3); gd.addNumericField("Correlation size:", distortionParameters.correlationSize, 0); // 64 gd.addNumericField("Correlation size LWIR:", distortionParameters.correlationSizeLwir, 0); // 16 gd.addNumericField("Maximal correlation size:", distortionParameters.maximalCorrelationSize, 0); // 128 gd.addNumericField("Maximal correlation size LWIR:", distortionParameters.maximalCorrelationSizeLwir, 0); // 16 gd.addNumericField("Correlation Gauss width (relative):", distortionParameters.correlationGaussWidth, 3); gd.addCheckbox("Keep Gaussian width absolute when increasing FFT size",distortionParameters.absoluteCorrelationGaussWidth); // /phaseCorrelationFraction //// leave this number of zeros on teh margins of the window (toatal from both sides). If correlationGaussWidth>0 will // additionally multiply by Hamming gd.addNumericField("Leave zeros on the window margins (toatal numbedr from both sides)", distortionParameters.zeros, 0); gd.addNumericField("Phase correlation modifier (1.0 - phase corr., 0 - just corr.)", distortionParameters.phaseCorrelationFraction, 5); gd.addNumericField("Correlation high-pass sigma:", distortionParameters.correlationHighPassSigma, 3); gd.addNumericField("Correlation low-pass sigma (fraction of sqrt(2)*Nyquist, lower - more filtering, 0 -none):",distortionParameters.correlationLowPassSigma, 3); gd.addNumericField("Correlation maximal offset from predicted:",distortionParameters.correlationMaxOffset, 3); gd.addNumericField("Detection ring width (fraction):", distortionParameters.correlationRingWidth, 3); gd.addNumericField("Correlation minimal contrast (normalized)", distortionParameters.correlationMinContrast, 3); gd.addNumericField("Correlation minimal contrast for initial search (normalized)", distortionParameters.correlationMinInitialContrast, 3); gd.addNumericField("Correlation minimal contrast (absolute)", distortionParameters.correlationMinAbsoluteContrast, 3); gd.addNumericField("Correlation minimal contrast for initial search (absolute)", distortionParameters.correlationMinAbsoluteInitialContrast, 3); gd.addNumericField("Decrease contrast of cells that are too close to the border to be processed in refinement pass", distortionParameters.scaleFirstPassContrast, 3); gd.addNumericField("Gaussian sigma to select correlation center in pixels, 2.0", distortionParameters.contrastSelectSigmaCenter, 3); gd.addNumericField("Gaussian sigma to select correlation off-centers (fraction of UV period), 0.1", distortionParameters.contrastSelectSigma, 3); gd.addNumericField("Gaussian sigma to average correlation variations (as contrast reference), 0.5", distortionParameters.contrastAverageSigma, 3); gd.addNumericField("Minimal initial pattern cluster size (0 - disable retries)", distortionParameters.minimalPatternCluster, 0); // 40 gd.addNumericField("Minimal initial LWIR pattern cluster size (0 - disable retries)", distortionParameters.minimalPatternClusterLwir, 0); // 10 gd.addMessage("thresholdContrast=" + threshold_contrast); gd.addMessage("threshold_number=" + threshold_number); gd.addNumericField("Scale minimal contrast if the initial cluster is nonzero but smaller", distortionParameters.scaleMinimalInitialContrast, 3); gd.addNumericField("Overlap of FFT areas when searching for pattern", distortionParameters.searchOverlap, 3); gd.addNumericField("Pattern subdivision:", distortionParameters.patternSubdiv, 0); // 4 gd.addNumericField("Blur pattern bitmap (sigma): ", distortionParameters.bPatternSigma, 3,5,"pattern cell"); // 0.02 gd.addNumericField("Blur pattern (sigma): ", distortionParameters.barraySigma, 3,5,"sensor pix"); // 0.5 gd.addNumericField("Correlation weights (around maximum):", distortionParameters.correlationWeightSigma, 3,5,"nodes"); // 2.5 gd.addNumericField("Correlation radius scale (0 - sharp sigma)", distortionParameters.correlationRadiusScale, 1,3,"sigmas"); //2.0 gd.addNumericField("Correlation maximal radius to use", distortionParameters.correlationRadius, 0,1,"pix"); // 2.0 gd.addNumericField("Correlation maximum calculation threshold", distortionParameters.correlationThreshold*100, 2,5,"%"); // .8 gd.addNumericField("Interpolate correlation (FFT*linear)", distortionParameters.correlationSubdiv, 0,3,"x"); // 16 gd.addNumericField("Interpolate correlation with FFT", distortionParameters.correlationFFTSubdiv, 0,3,"x"); // 4 gd.addNumericField("Correlation dx (debug)", distortionParameters.correlationDx, 3); gd.addNumericField("Correlation dy (debug)", distortionParameters.correlationDy, 3); gd.addNumericField("Maximal size of the pattern grid (square)", distortionParameters.gridSize, 0); gd.addCheckbox ("Refine correlations", distortionParameters.refineCorrelations); gd.addCheckbox ("Use fast correlation on first pass", distortionParameters.fastCorrelationOnFirstPass); gd.addCheckbox ("Use fast correlation on refine pass", distortionParameters.fastCorrelationOnFinalPass); gd.addCheckbox ("Average correlation measurements between neighbors (on refine)", distortionParameters.correlationAverageOnRefine); gd.addCheckbox ("Update coordinates of the grid points as they are recalculated (false - then update all at once)", distortionParameters.refineInPlace); gd.addNumericField("Distance to ortho neighbors (for averaging)", distortionParameters.averageOrthoDist, 3,5,"sensor pix"); gd.addNumericField("Combined weight of ortho neighbors (fraction of 1.0)", distortionParameters.averageOrthoWeight, 3); gd.addNumericField("Distance to diagonal neighbors (for averaging)", distortionParameters.averageDiagDist, 3,5,"sensor pix"); gd.addNumericField("Combined weight of diagonal neighbors (fraction of 1.0)", distortionParameters.averageDiagWeight, 3); gd.addCheckbox ("Use quadratic extrapolation (false - force linear)", distortionParameters.useQuadratic); gd.addCheckbox ("Remove outer (unreliable) layer before extrapolation", distortionParameters.removeLast); gd.addNumericField("Number of extrapolated layers of nodes (final stage)", distortionParameters.numberExtrapolated, 0); gd.addNumericField("Sigma during final extrapolation stage", distortionParameters.extrapolationSigma, 3,5,"nodes"); gd.addNumericField("Minimal UV span in correlation window to trigger FFT size increase", distortionParameters.minUVSpan, 3); gd.addCheckbox ("Compensate uneven pattern intensity", distortionParameters.flatFieldCorrection); gd.addNumericField("Extrapolate pattern intensity map (relative to pattern period)", distortionParameters.flatFieldExtarpolate, 3); gd.addNumericField("Blur pattern intensity map (relative to pattern period)", distortionParameters.flatFieldBlur, 3); gd.addNumericField("Do not use areas where intensity map is below this part of maximal", distortionParameters.flatFieldMin, 3); gd.addNumericField("Shrink before extrapolating intensity map (relative to the average grid period)", distortionParameters.flatFieldShrink, 3); gd.addNumericField("Expand during extrapolation (relative to the average grid period)", distortionParameters.flatFieldExpand, 3); gd.addNumericField("Extrapolation weight effective radius (relative to the average grid period)", distortionParameters.flatFieldSigmaRadius, 3); gd.addNumericField("Consider pixels in a square with the side twice this (relative to flatFieldSigmaRadius)", distortionParameters.flatFieldExtraRadius, 3); gd.addNumericField("Multiply the average grid period to determine the area for averaging the grig brightness", distortionParameters.averagingAreaScale, 3); gd.addCheckbox ("Legacy mode (deprecated)", distortionParameters.legacyMode); gd.addNumericField("Debug level inside the loop", distortionParameters.loop_debug_level, 0); gd.addNumericField("Debug Level:", mdl[0], 0); WindowTools.addScrollBars(gd); gd.showDialog(); if (gd.wasCanceled()) return false; distortionParameters.FFTSize = makePowerOfTwo((int) gd.getNextNumber()); distortionParameters.FFTSize_lwir = makePowerOfTwo((int) gd.getNextNumber()); distortionParameters.FFTOverlap = makePowerOfTwo((int) gd.getNextNumber()); distortionParameters.FFTOverlap_lwir = makePowerOfTwo((int) gd.getNextNumber()); distortionParameters.fftGaussWidth= gd.getNextNumber(); distortionParameters.correlationSize = makePowerOfTwo((int) gd.getNextNumber()); distortionParameters.correlationSizeLwir = makePowerOfTwo((int) gd.getNextNumber()); distortionParameters.maximalCorrelationSize = makePowerOfTwo((int) gd.getNextNumber()); distortionParameters.maximalCorrelationSizeLwir = makePowerOfTwo((int) gd.getNextNumber()); distortionParameters.correlationGaussWidth= gd.getNextNumber(); distortionParameters.absoluteCorrelationGaussWidth=gd.getNextBoolean(); distortionParameters.zeros= (int) gd.getNextNumber(); distortionParameters.phaseCorrelationFraction= gd.getNextNumber(); distortionParameters.correlationHighPassSigma= gd.getNextNumber(); distortionParameters.correlationLowPassSigma= gd.getNextNumber(); distortionParameters.correlationMaxOffset= gd.getNextNumber(); distortionParameters.correlationRingWidth= gd.getNextNumber(); distortionParameters.correlationMinContrast= gd.getNextNumber(); distortionParameters.correlationMinInitialContrast= gd.getNextNumber(); distortionParameters.correlationMinAbsoluteContrast= gd.getNextNumber(); distortionParameters.correlationMinAbsoluteInitialContrast= gd.getNextNumber(); distortionParameters.scaleFirstPassContrast= gd.getNextNumber(); distortionParameters.contrastSelectSigmaCenter= gd.getNextNumber(); distortionParameters.contrastSelectSigma= gd.getNextNumber(); distortionParameters.contrastAverageSigma= gd.getNextNumber(); distortionParameters.minimalPatternCluster=(int) gd.getNextNumber(); distortionParameters.minimalPatternClusterLwir=(int) gd.getNextNumber(); distortionParameters.scaleMinimalInitialContrast=gd.getNextNumber(); distortionParameters.searchOverlap= gd.getNextNumber(); distortionParameters.patternSubdiv= (int) gd.getNextNumber(); distortionParameters.bPatternSigma= gd.getNextNumber(); distortionParameters.barraySigma= gd.getNextNumber(); distortionParameters.correlationWeightSigma= gd.getNextNumber(); distortionParameters.correlationRadiusScale= gd.getNextNumber(); distortionParameters.correlationRadius= (int) gd.getNextNumber(); distortionParameters.correlationThreshold= 0.01*gd.getNextNumber(); distortionParameters.correlationSubdiv= (int) gd.getNextNumber(); distortionParameters.correlationFFTSubdiv=1; for (i=(int) gd.getNextNumber(); i >1; i>>=1) distortionParameters.correlationFFTSubdiv <<=1; /* make it to be power of 2 */ distortionParameters.correlationDx= gd.getNextNumber(); distortionParameters.correlationDy= gd.getNextNumber(); distortionParameters.gridSize= (int) gd.getNextNumber(); distortionParameters.refineCorrelations= gd.getNextBoolean(); distortionParameters.fastCorrelationOnFirstPass=gd.getNextBoolean(); distortionParameters.fastCorrelationOnFinalPass=gd.getNextBoolean(); distortionParameters.correlationAverageOnRefine=gd.getNextBoolean(); distortionParameters.refineInPlace= gd.getNextBoolean(); distortionParameters.averageOrthoDist= gd.getNextNumber(); distortionParameters.averageOrthoWeight= gd.getNextNumber(); distortionParameters.averageDiagDist= gd.getNextNumber(); distortionParameters.averageDiagWeight= gd.getNextNumber(); distortionParameters.useQuadratic= gd.getNextBoolean(); distortionParameters.removeLast= gd.getNextBoolean(); distortionParameters.numberExtrapolated=(int) gd.getNextNumber(); distortionParameters.extrapolationSigma= gd.getNextNumber(); distortionParameters.minUVSpan= gd.getNextNumber(); distortionParameters.flatFieldCorrection= gd.getNextBoolean(); distortionParameters.flatFieldExtarpolate= gd.getNextNumber(); distortionParameters.flatFieldBlur= gd.getNextNumber(); distortionParameters.flatFieldMin= gd.getNextNumber(); distortionParameters.flatFieldShrink= gd.getNextNumber(); distortionParameters.flatFieldExpand= gd.getNextNumber(); distortionParameters.flatFieldSigmaRadius= gd.getNextNumber(); distortionParameters.flatFieldExtraRadius= gd.getNextNumber(); distortionParameters.averagingAreaScale= gd.getNextNumber(); distortionParameters.legacyMode= gd.getNextBoolean(); distortionParameters.loop_debug_level= (int) gd.getNextNumber(); mdl[0]= (int) gd.getNextNumber(); return true; } private int makePowerOfTwo(int v) { int v2 = 1; for (int i=v; i > 1; i>>=1 ) v2 <<=1; /* make it to be power of 2 */ return v2; } public DistortionParameters(int correlationSize, int correlationSizeLwir, int maximalCorrelationSize, int maximalCorrelationSizeLwir, double correlationGaussWidth, boolean absoluteCorrelationGaussWidth, int zeros, int FFTSize, int FFTSize_lwir, int FFTOverlap, int FFTOverlap_lwir, double fftGaussWidth, double phaseCorrelationFraction, double correlationHighPassSigma, double correlationLowPassSigma, double correlationRingWidth, double correlationMaxOffset, // maximal distance between predicted and // actual pattern node double correlationMinContrast, // minimal contrast for the pattern to pass double correlationMinInitialContrast, // minimal contrast for the pattern of the center (initial point) double correlationMinAbsoluteContrast, // minimal contrast for the pattern to pass, does not compensate // for low ligt double correlationMinAbsoluteInitialContrast, // minimal contrast for the pattern of the center (initial // point) double scaleFirstPassContrast, // Decrease contrast of cells that are too close to the border to be // processed in refinement pass double contrastSelectSigmaCenter, // Gaussian sigma to select correlation centers (fraction of UV // period), 0.02 (center spot) double contrastSelectSigma, // Gaussian sigma to select correlation centers (fraction of UV period), 0.1 double contrastAverageSigma, // Gaussian sigma to average correlation variations (as contrast reference) // 0.5 int minimalPatternCluster, // minimal pattern cluster size (0 - disable retries) int minimalPatternClusterLwir, // minimal pattern cluster size (0 - disable retries) double scaleMinimalInitialContrast, // increase/decrease minimal contrast if initial cluster is >0 but // less than minimalPatternCluster double searchOverlap, // when searching for grid, step this amount of the FFTSize int patternSubdiv, double correlationDx, double correlationDy, int gridSize, int loop_debug_level, boolean refineCorrelations, boolean fastCorrelationOnFirstPass, // use fast (less precise) correlation // on first pass boolean fastCorrelationOnFinalPass, // use fast (less precise) correlation on refine pass double bPatternSigma, // blur bPattern with this sigma double barraySigma, // blur barray with this sigma, multiplied by subdiv double correlationWeightSigma, // sigma (in pixels) for maximum approximation double correlationRadiusScale, // maximal radius to consider, in sigmas (if 0 - use sigma as radius) int correlationRadius, // radius (green pixel) of the correlation maximum to use for x/y measurement double correlationThreshold, // fraction of the value of the maximum fro the point to be included in // centroid calculation int correlationSubdiv, // Total subdivision of the correlation maximum (linear and FFT) int correlationFFTSubdiv, boolean correlationAverageOnRefine, // average position between neighbor // samples boolean refineInPlace, // Update coordinates of the grid points as they are recalculated (false - then // update all at once) double averageOrthoDist, // distance to up/down/right left neighbors (0.5) double averageOrthoWeight, // weight of 4 ortho neighbors (combined) - 0.4), weight of center -s // 1.0-averageOrthoWeight-averageDiagWeight double averageDiagDist, // distance to diagonal neighbors (projection on x/y) (0.5) double averageDiagWeight, // weight of 4 diagonal neighbors (combined) - 0.4) boolean useQuadratic, // use quadratic extrapolation to predict position/wave vectors of a new pixel // (false - use linear) boolean removeLast, // remove outer (unreliable) row of nodes int numberExtrapolated, // add this number of extrapolated nodes double extrapolationSigma, // use instead of the correlationWeightSigma during final extrapolation double minUVSpan, // Minimal u/v span in correlation window that triggers increase of the // correlation FFT size boolean flatFieldCorrection, // compensate grid uneven intensity (vignetting, illumination) double flatFieldExtarpolate, // extrapolate flat field intensity map (relative to the average grid // period) double flatFieldBlur, // blur the intensity map (relative to the average grid period) double flatFieldMin, // do not try to compensate if intensity less than this part of maximal double flatFieldShrink, // Shrink before extrapolating intensity map (relative to the average grid // period) double flatFieldExpand, // Expand during extrapolation (relative to the average grid period) double flatFieldSigmaRadius, // Extrapolation weight effective radius (relative to the average grid // period) double flatFieldExtraRadius, // Consider pixels in a square with the side twice this (relative to // flatFieldSigmaRadius) double averagingAreaScale, // multiply the average grid period to determine the area for averaging the // grig brightness boolean legacyMode) { this.correlationSize = correlationSize; this.correlationSizeLwir = correlationSizeLwir; this.maximalCorrelationSize = maximalCorrelationSize; this.maximalCorrelationSizeLwir = maximalCorrelationSizeLwir; this.correlationGaussWidth = correlationGaussWidth; this.absoluteCorrelationGaussWidth = absoluteCorrelationGaussWidth; this.zeros = zeros; this.FFTSize = FFTSize; this.FFTSize_lwir = FFTSize_lwir; this.FFTOverlap = FFTOverlap; this.FFTOverlap_lwir = FFTOverlap_lwir; this.fftGaussWidth = fftGaussWidth; this.phaseCorrelationFraction = phaseCorrelationFraction; this.correlationHighPassSigma = correlationHighPassSigma; this.correlationLowPassSigma = correlationLowPassSigma; this.correlationRingWidth = correlationRingWidth; this.correlationMaxOffset = correlationMaxOffset; this.correlationMinContrast = correlationMinContrast; this.correlationMinInitialContrast = correlationMinInitialContrast; this.correlationMinAbsoluteContrast = correlationMinAbsoluteContrast; // minimal contrast for the pattern to // pass, does not compensate for low // ligt this.correlationMinAbsoluteInitialContrast = correlationMinAbsoluteInitialContrast; // minimal contrast for // the pattern of the // center (initial // point) this.scaleFirstPassContrast = scaleFirstPassContrast; // Decrease contrast of cells that are too close to // the border to be processed in refinement pass this.contrastSelectSigmaCenter = contrastSelectSigmaCenter; // Gaussian sigma to select correlation centers // (pixels, 2.0) this.contrastSelectSigma = contrastSelectSigma; // Gaussian sigma to select correlation centers (fraction of // UV period), 0.1 this.contrastAverageSigma = contrastAverageSigma; // Gaussian sigma to average correlation variations (as // contrast reference) 0.5 this.minimalPatternCluster = minimalPatternCluster; // minimal pattern cluster size (0 - disable retries) this.minimalPatternClusterLwir = minimalPatternClusterLwir; // minimal pattern cluster size (0 - disable // retries) this.scaleMinimalInitialContrast = scaleMinimalInitialContrast; // increase/decrease minimal contrast if // initial cluster is >0 but less than // minimalPatternCluster this.searchOverlap = searchOverlap; // when searching for grid, step this amount of the FFTSize this.patternSubdiv = patternSubdiv; this.correlationDx = correlationDx; this.correlationDy = correlationDy; this.gridSize = gridSize; this.loop_debug_level = loop_debug_level; this.refineCorrelations = refineCorrelations; this.fastCorrelationOnFirstPass = fastCorrelationOnFirstPass; this.fastCorrelationOnFinalPass = fastCorrelationOnFinalPass; this.bPatternSigma = bPatternSigma; // overwrites SimulationParameters.bPatternSigma this.barraySigma = barraySigma; this.correlationWeightSigma = correlationWeightSigma; this.correlationRadiusScale = correlationRadiusScale; this.correlationRadius = correlationRadius; this.correlationThreshold = correlationThreshold; this.correlationSubdiv = correlationSubdiv; this.correlationFFTSubdiv = correlationFFTSubdiv; this.correlationAverageOnRefine = correlationAverageOnRefine; this.refineInPlace = refineInPlace; this.averageOrthoDist = averageOrthoDist; this.averageOrthoWeight = averageOrthoWeight; this.averageDiagDist = averageDiagDist; this.averageDiagWeight = averageDiagWeight; this.useQuadratic = useQuadratic; this.removeLast = removeLast; this.numberExtrapolated = numberExtrapolated; this.extrapolationSigma = extrapolationSigma; this.minUVSpan = minUVSpan; this.flatFieldCorrection = flatFieldCorrection; // compensate grid uneven intensity (vignetting, // illumination) this.flatFieldExtarpolate = flatFieldExtarpolate; // extrapolate flat field intensity map (relative to the // average grid period) this.flatFieldBlur = flatFieldBlur; // blur the intensity map (relative to the average grid period) this.flatFieldMin = flatFieldMin; this.flatFieldShrink = flatFieldShrink; this.flatFieldExpand = flatFieldExpand; this.flatFieldSigmaRadius = flatFieldSigmaRadius; this.flatFieldExtraRadius = flatFieldExtraRadius; this.averagingAreaScale = averagingAreaScale; this.legacyMode = legacyMode; } @Override public DistortionParameters clone() { return new DistortionParameters(this.correlationSize, this.correlationSizeLwir, this.maximalCorrelationSize, this.maximalCorrelationSizeLwir, this.correlationGaussWidth, this.absoluteCorrelationGaussWidth, this.zeros, this.FFTSize, this.FFTSize_lwir, this.FFTOverlap, this.FFTOverlap_lwir, this.fftGaussWidth, this.phaseCorrelationFraction, this.correlationHighPassSigma, this.correlationLowPassSigma, this.correlationRingWidth, this.correlationMaxOffset, // maximal // distance // between // predicted and // actual // pattern node this.correlationMinContrast, // minimal contrast for the pattern to pass this.correlationMinInitialContrast, this.correlationMinAbsoluteContrast, // minimal contrast for the // pattern to pass, does // not compensate for // low ligt this.correlationMinAbsoluteInitialContrast, // minimal contrast for the pattern of the center // (initial point) this.scaleFirstPassContrast, // Decrease contrast of cells that are too close to the border to be // processed in refinement pass this.contrastSelectSigmaCenter, // Gaussian sigma to select correlation centers (pixels, 2.0) this.contrastSelectSigma, // Gaussian sigma to select correlation centers (fraction of UV period), // 0.1 this.contrastAverageSigma, // Gaussian sigma to average correlation variations (as contrast // reference) 0.5 this.minimalPatternCluster, // minimal pattern cluster size (0 - disable retries) this.minimalPatternClusterLwir, // minimal pattern cluster size (0 - disable retries) this.scaleMinimalInitialContrast, // increase/decrease minimal contrast if initial cluster is >0 but // less than minimalPatternCluster this.searchOverlap, // when searching for grid, step this amount of the FFTSize this.patternSubdiv, this.correlationDx, this.correlationDy, this.gridSize, this.loop_debug_level, this.refineCorrelations, this.fastCorrelationOnFirstPass, // use fast (less precise) correlation on // first pass this.fastCorrelationOnFinalPass, // use fast (less precise) correlation on refine pass this.bPatternSigma, // blur bPattern with this sigma this.barraySigma, this.correlationWeightSigma, // sigma (in pixels) for maximum approximation this.correlationRadiusScale, // maximal radius to consider, in sigmas (if 0 - use sigma as radius) this.correlationRadius, // radius (green pixel) of the correlation maximum to use for x/y // measurement this.correlationThreshold, this.correlationSubdiv, // Total subdivision of the correlation maximum // (linear and FFT) this.correlationFFTSubdiv, this.correlationAverageOnRefine, // average position between neighbor // samples this.refineInPlace, // Update coordinates of the grid points as they are recalculated (false - then // update all at once) this.averageOrthoDist, // distance to up/down/right left neighbors (0.5) this.averageOrthoWeight, // weight of 4 ortho neighbors (combined) - 0.4), weight of center -s // 1.0-averageOrthoWeight-averageDiagWeight this.averageDiagDist, // distance to diagonal neighbors (projection on x/y) (0.5) this.averageDiagWeight, // weight of 4 diagonal neighbors (combined) - 0.4) this.useQuadratic, // use quadratic extrapolation to predict position/wave vectors of a new pixel // (false - use linear) this.removeLast, // remove outer (unreliable) row of nodes this.numberExtrapolated, // add this number of extrapolated nodes this.extrapolationSigma, // use instead of the correlationWeightSigma during final extrapolation this.minUVSpan, // Minimal u/v span in correlation window that triggers increase of the // correlation FFT size this.flatFieldCorrection, // compensate grid uneven intensity (vignetting, illumination) this.flatFieldExtarpolate, // extrapolate flat field intensity map (relative to the average grid // period) this.flatFieldBlur, // blur the intensity map (relative to the average grid period) this.flatFieldMin, this.flatFieldShrink, this.flatFieldExpand, this.flatFieldSigmaRadius, this.flatFieldExtraRadius, this.averagingAreaScale, this.legacyMode); } public void setProperties(String prefix, Properties properties) { properties.setProperty(prefix + "correlationSize", this.correlationSize + ""); properties.setProperty(prefix + "correlationSizeLwir", this.correlationSizeLwir + ""); properties.setProperty(prefix + "maximalCorrelationSize", this.maximalCorrelationSize + ""); properties.setProperty(prefix + "maximalCorrelationSizeLwir", this.maximalCorrelationSizeLwir + ""); properties.setProperty(prefix + "correlationGaussWidth", this.correlationGaussWidth + ""); properties.setProperty(prefix + "absoluteCorrelationGaussWidth", this.absoluteCorrelationGaussWidth + ""); properties.setProperty(prefix + "zeros", this.zeros + ""); properties.setProperty(prefix + "FFTSize", this.FFTSize + ""); properties.setProperty(prefix + "FFTSize_lwir", this.FFTSize_lwir + ""); properties.setProperty(prefix + "FFTOverlap", this.FFTOverlap + ""); properties.setProperty(prefix + "FFTOverlap_lwir", this.FFTOverlap_lwir + ""); properties.setProperty(prefix + "fftGaussWidth", this.fftGaussWidth + ""); properties.setProperty(prefix + "phaseCorrelationFraction", this.phaseCorrelationFraction + ""); properties.setProperty(prefix + "correlationHighPassSigma", this.correlationHighPassSigma + ""); properties.setProperty(prefix + "correlationLowPassSigma", this.correlationLowPassSigma + ""); properties.setProperty(prefix + "correlationRingWidth", this.correlationRingWidth + ""); properties.setProperty(prefix + "correlationMaxOffset", this.correlationMaxOffset + ""); properties.setProperty(prefix + "correlationMinContrast", this.correlationMinContrast + ""); properties.setProperty(prefix + "correlationMinInitialContrast", this.correlationMinInitialContrast + ""); properties.setProperty(prefix + "correlationMinAbsoluteContrast", this.correlationMinAbsoluteContrast + ""); properties.setProperty(prefix + "correlationMinAbsoluteInitialContrast", this.correlationMinAbsoluteInitialContrast + ""); properties.setProperty(prefix + "scaleFirstPassContrast", this.scaleFirstPassContrast + ""); properties.setProperty(prefix + "contrastSelectSigmaCenter", this.contrastSelectSigmaCenter + ""); properties.setProperty(prefix + "contrastSelectSigma", this.contrastSelectSigma + ""); properties.setProperty(prefix + "contrastAverageSigma", this.contrastAverageSigma + ""); properties.setProperty(prefix + "minimalPatternCluster", this.minimalPatternCluster + ""); properties.setProperty(prefix + "minimalPatternClusterLwir", this.minimalPatternClusterLwir + ""); properties.setProperty(prefix + "scaleMinimalInitialContrast", this.scaleMinimalInitialContrast + ""); properties.setProperty(prefix + "searchOverlap", this.searchOverlap + ""); properties.setProperty(prefix + "patternSubdiv", this.patternSubdiv + ""); properties.setProperty(prefix + "correlationDx", this.correlationDx + ""); properties.setProperty(prefix + "correlationDy", this.correlationDy + ""); properties.setProperty(prefix + "gridSize", this.gridSize + ""); properties.setProperty(prefix + "loop_debug_level", this.loop_debug_level + ""); properties.setProperty(prefix + "refineCorrelations", this.refineCorrelations + ""); properties.setProperty(prefix + "fastCorrelationOnFirstPass", this.fastCorrelationOnFirstPass + ""); properties.setProperty(prefix + "fastCorrelationOnFinalPass", this.fastCorrelationOnFinalPass + ""); properties.setProperty(prefix + "bPatternSigma", this.bPatternSigma + ""); properties.setProperty(prefix + "barraySigma", this.barraySigma + ""); properties.setProperty(prefix + "correlationWeightSigma", this.correlationWeightSigma + ""); properties.setProperty(prefix + "correlationRadiusScale", this.correlationRadiusScale + ""); properties.setProperty(prefix + "correlationRadius", this.correlationRadius + ""); properties.setProperty(prefix + "correlationThreshold", this.correlationThreshold + ""); properties.setProperty(prefix + "correlationSubdiv", this.correlationSubdiv + ""); properties.setProperty(prefix + "correlationFFTSubdiv", this.correlationFFTSubdiv + ""); properties.setProperty(prefix + "correlationAverageOnRefine", this.correlationAverageOnRefine + ""); properties.setProperty(prefix + "refineInPlace", this.refineInPlace + ""); properties.setProperty(prefix + "averageOrthoDist", this.averageOrthoDist + ""); properties.setProperty(prefix + "averageOrthoWeight", this.averageOrthoWeight + ""); properties.setProperty(prefix + "averageDiagDist", this.averageDiagDist + ""); properties.setProperty(prefix + "averageDiagWeight", this.averageDiagWeight + ""); properties.setProperty(prefix + "useQuadratic", this.useQuadratic + ""); properties.setProperty(prefix + "removeLast", this.removeLast + ""); properties.setProperty(prefix + "numberExtrapolated", this.numberExtrapolated + ""); properties.setProperty(prefix + "extrapolationSigma", this.extrapolationSigma + ""); properties.setProperty(prefix + "minUVSpan", this.minUVSpan + ""); properties.setProperty(prefix + "flatFieldCorrection", this.flatFieldCorrection + ""); properties.setProperty(prefix + "flatFieldExtarpolate", this.flatFieldExtarpolate + ""); properties.setProperty(prefix + "flatFieldBlur", this.flatFieldBlur + ""); properties.setProperty(prefix + "flatFieldMin", this.flatFieldMin + ""); properties.setProperty(prefix + "flatFieldShrink", this.flatFieldShrink + ""); properties.setProperty(prefix + "flatFieldExpand", this.flatFieldExpand + ""); properties.setProperty(prefix + "flatFieldSigmaRadius", this.flatFieldSigmaRadius + ""); properties.setProperty(prefix + "flatFieldExtraRadius", this.flatFieldExtraRadius + ""); properties.setProperty(prefix + "averagingAreaScale", this.averagingAreaScale + ""); properties.setProperty(prefix + "legacyMode", this.minUVSpan + ""); } public void getProperties(String prefix, Properties properties) { // EProperties properties = (EProperties) pproperties; if (properties.getProperty(prefix + "correlationSize") != null) this.correlationSize = Integer.parseInt(properties.getProperty(prefix + "correlationSize")); if (properties.getProperty(prefix + "correlationSizeLwir") != null) this.correlationSizeLwir = Integer.parseInt(properties.getProperty(prefix + "correlationSizeLwir")); if (properties.getProperty(prefix + "maximalCorrelationSize") != null) this.maximalCorrelationSize = Integer .parseInt(properties.getProperty(prefix + "maximalCorrelationSize")); if (properties.getProperty(prefix + "maximalCorrelationSizeLwir") != null) this.maximalCorrelationSizeLwir = Integer .parseInt(properties.getProperty(prefix + "maximalCorrelationSizeLwir")); if (properties.getProperty(prefix + "correlationGaussWidth") != null) this.correlationGaussWidth = Double .parseDouble(properties.getProperty(prefix + "correlationGaussWidth")); if (properties.getProperty(prefix + "FFTSize") != null) this.FFTSize = Integer.parseInt(properties.getProperty(prefix + "FFTSize")); if (properties.getProperty(prefix + "FFTSize_lwir") != null) this.FFTSize_lwir = Integer.parseInt(properties.getProperty(prefix + "FFTSize_lwir")); if (properties.getProperty(prefix + "FFTOverlap") != null) this.FFTOverlap = Integer.parseInt(properties.getProperty(prefix + "FFTOverlap")); if (properties.getProperty(prefix + "FFTOverlap_lwir") != null) this.FFTOverlap_lwir = Integer.parseInt(properties.getProperty(prefix + "FFTOverlap_lwir")); // finally shortened : // this.FFTOverlap= properties.getProperty(prefix+"FFTOverlap", this.FFTOverlap); // this.FFTOverlap_lwir= properties.getProperty(prefix+"FFTOverlap_lwir",this.FFTOverlap_lwir); if (properties.getProperty(prefix + "absoluteCorrelationGaussWidth") != null) this.absoluteCorrelationGaussWidth = Boolean .parseBoolean(properties.getProperty(prefix + "absoluteCorrelationGaussWidth")); if (properties.getProperty(prefix + "zeros") != null) this.zeros = Integer.parseInt(properties.getProperty(prefix + "zeros")); if (properties.getProperty(prefix + "fftGaussWidth") != null) this.fftGaussWidth = Double.parseDouble(properties.getProperty(prefix + "fftGaussWidth")); if (properties.getProperty(prefix + "phaseCorrelationFraction") != null) this.phaseCorrelationFraction = Double .parseDouble(properties.getProperty(prefix + "phaseCorrelationFraction")); if (properties.getProperty(prefix + "correlationHighPassSigma") != null) this.correlationHighPassSigma = Double .parseDouble(properties.getProperty(prefix + "correlationHighPassSigma")); if (properties.getProperty(prefix + "correlationLowPassSigma") != null) this.correlationLowPassSigma = Double .parseDouble(properties.getProperty(prefix + "correlationLowPassSigma")); if (properties.getProperty(prefix + "correlationRingWidth") != null) this.correlationRingWidth = Double.parseDouble(properties.getProperty(prefix + "correlationRingWidth")); if (properties.getProperty(prefix + "correlationMaxOffset") != null) this.correlationMaxOffset = Double.parseDouble(properties.getProperty(prefix + "correlationMaxOffset")); if (properties.getProperty(prefix + "correlationMinContrast") != null) this.correlationMinContrast = Double .parseDouble(properties.getProperty(prefix + "correlationMinContrast")); if (properties.getProperty(prefix + "correlationMinInitialContrast") != null) this.correlationMinInitialContrast = Double .parseDouble(properties.getProperty(prefix + "correlationMinInitialContrast")); if (properties.getProperty(prefix + "correlationMinAbsoluteContrast") != null) this.correlationMinAbsoluteContrast = Double .parseDouble(properties.getProperty(prefix + "correlationMinAbsoluteContrast")); if (properties.getProperty(prefix + "correlationMinAbsoluteInitialContrast") != null) this.correlationMinAbsoluteInitialContrast = Double .parseDouble(properties.getProperty(prefix + "correlationMinAbsoluteInitialContrast")); if (properties.getProperty(prefix + "scaleFirstPassContrast") != null) this.scaleFirstPassContrast = Double .parseDouble(properties.getProperty(prefix + "scaleFirstPassContrast")); if (properties.getProperty(prefix + "contrastSelectSigmaCenter") != null) this.contrastSelectSigmaCenter = Double .parseDouble(properties.getProperty(prefix + "contrastSelectSigmaCenter")); if (properties.getProperty(prefix + "contrastSelectSigma") != null) this.contrastSelectSigma = Double.parseDouble(properties.getProperty(prefix + "contrastSelectSigma")); if (properties.getProperty(prefix + "contrastAverageSigma") != null) this.contrastAverageSigma = Double.parseDouble(properties.getProperty(prefix + "contrastAverageSigma")); if (properties.getProperty(prefix + "minimalPatternCluster") != null) this.minimalPatternCluster = Integer.parseInt(properties.getProperty(prefix + "minimalPatternCluster")); if (properties.getProperty(prefix + "minimalPatternClusterLwir") != null) this.minimalPatternClusterLwir = Integer .parseInt(properties.getProperty(prefix + "minimalPatternClusterLwir")); if (properties.getProperty(prefix + "scaleMinimalInitialContrast") != null) this.scaleMinimalInitialContrast = Double .parseDouble(properties.getProperty(prefix + "scaleMinimalInitialContrast")); if (properties.getProperty(prefix + "searchOverlap") != null) this.searchOverlap = Double.parseDouble(properties.getProperty(prefix + "searchOverlap")); if (properties.getProperty(prefix + "patternSubdiv") != null) this.patternSubdiv = Integer.parseInt(properties.getProperty(prefix + "patternSubdiv")); if (properties.getProperty(prefix + "correlationDx") != null) this.correlationDx = Double.parseDouble(properties.getProperty(prefix + "correlationDx")); if (properties.getProperty(prefix + "correlationDy") != null) this.correlationDy = Double.parseDouble(properties.getProperty(prefix + "correlationDy")); if (properties.getProperty(prefix + "gridSize") != null) this.gridSize = Integer.parseInt(properties.getProperty(prefix + "gridSize")); if (properties.getProperty(prefix + "loop_debug_level") != null) this.loop_debug_level = Integer.parseInt(properties.getProperty(prefix + "loop_debug_level")); if (properties.getProperty(prefix + "refineCorrelations") != null) this.refineCorrelations = Boolean.parseBoolean(properties.getProperty(prefix + "refineCorrelations")); if (properties.getProperty(prefix + "fastCorrelationOnFirstPass") != null) this.fastCorrelationOnFirstPass = Boolean .parseBoolean(properties.getProperty(prefix + "fastCorrelationOnFirstPass")); if (properties.getProperty(prefix + "fastCorrelationOnFinalPass") != null) this.fastCorrelationOnFinalPass = Boolean .parseBoolean(properties.getProperty(prefix + "fastCorrelationOnFinalPass")); if (properties.getProperty(prefix + "bPatternSigma") != null) this.bPatternSigma = Double.parseDouble(properties.getProperty(prefix + "bPatternSigma")); if (properties.getProperty(prefix + "barraySigma") != null) this.barraySigma = Double.parseDouble(properties.getProperty(prefix + "barraySigma")); if (properties.getProperty(prefix + "correlationWeightSigma") != null) this.correlationWeightSigma = Double .parseDouble(properties.getProperty(prefix + "correlationWeightSigma")); if (properties.getProperty(prefix + "correlationRadiusScale") != null) this.correlationRadiusScale = Double .parseDouble(properties.getProperty(prefix + "correlationRadiusScale")); if (properties.getProperty(prefix + "correlationRadius") != null) this.correlationRadius = Integer.parseInt(properties.getProperty(prefix + "correlationRadius")); if (properties.getProperty(prefix + "correlationThreshold") != null) this.correlationThreshold = Double.parseDouble(properties.getProperty(prefix + "correlationThreshold")); if (properties.getProperty(prefix + "correlationSubdiv") != null) this.correlationSubdiv = Integer.parseInt(properties.getProperty(prefix + "correlationSubdiv")); if (properties.getProperty(prefix + "correlationFFTSubdiv") != null) this.correlationFFTSubdiv = Integer.parseInt(properties.getProperty(prefix + "correlationFFTSubdiv")); if (properties.getProperty(prefix + "correlationAverageOnRefine") != null) this.correlationAverageOnRefine = Boolean .parseBoolean(properties.getProperty(prefix + "correlationAverageOnRefine")); if (properties.getProperty(prefix + "refineInPlace") != null) this.refineInPlace = Boolean.parseBoolean(properties.getProperty(prefix + "refineInPlace")); if (properties.getProperty(prefix + "averageOrthoDist") != null) this.averageOrthoDist = Double.parseDouble(properties.getProperty(prefix + "averageOrthoDist")); if (properties.getProperty(prefix + "averageOrthoWeight") != null) this.averageOrthoWeight = Double.parseDouble(properties.getProperty(prefix + "averageOrthoWeight")); if (properties.getProperty(prefix + "averageDiagDist") != null) this.averageDiagDist = Double.parseDouble(properties.getProperty(prefix + "averageDiagDist")); if (properties.getProperty(prefix + "correlationRadiusScale") != null) this.averageDiagWeight = Double.parseDouble(properties.getProperty(prefix + "averageDiagWeight")); if (properties.getProperty(prefix + "useQuadratic") != null) this.useQuadratic = Boolean.parseBoolean(properties.getProperty(prefix + "useQuadratic")); if (properties.getProperty(prefix + "removeLast") != null) this.removeLast = Boolean.parseBoolean(properties.getProperty(prefix + "removeLast")); if (properties.getProperty(prefix + "numberExtrapolated") != null) this.numberExtrapolated = Integer.parseInt(properties.getProperty(prefix + "numberExtrapolated")); if (properties.getProperty(prefix + "extrapolationSigma") != null) this.extrapolationSigma = Double.parseDouble(properties.getProperty(prefix + "extrapolationSigma")); if (properties.getProperty(prefix + "minUVSpan") != null) this.minUVSpan = Double.parseDouble(properties.getProperty(prefix + "minUVSpan")); if (properties.getProperty(prefix + "flatFieldCorrection") != null) this.flatFieldCorrection = Boolean.parseBoolean(properties.getProperty(prefix + "flatFieldCorrection")); if (properties.getProperty(prefix + "flatFieldExtarpolate") != null) this.flatFieldExtarpolate = Double.parseDouble(properties.getProperty(prefix + "flatFieldExtarpolate")); if (properties.getProperty(prefix + "flatFieldBlur") != null) this.flatFieldBlur = Double.parseDouble(properties.getProperty(prefix + "flatFieldBlur")); if (properties.getProperty(prefix + "flatFieldMin") != null) this.flatFieldMin = Double.parseDouble(properties.getProperty(prefix + "flatFieldMin")); if (properties.getProperty(prefix + "flatFieldShrink") != null) this.flatFieldShrink = Double.parseDouble(properties.getProperty(prefix + "flatFieldShrink")); if (properties.getProperty(prefix + "flatFieldExpand") != null) this.flatFieldExpand = Double.parseDouble(properties.getProperty(prefix + "flatFieldExpand")); if (properties.getProperty(prefix + "flatFieldSigmaRadius") != null) this.flatFieldSigmaRadius = Double.parseDouble(properties.getProperty(prefix + "flatFieldSigmaRadius")); if (properties.getProperty(prefix + "flatFieldExtraRadius") != null) this.flatFieldExtraRadius = Double.parseDouble(properties.getProperty(prefix + "flatFieldExtraRadius")); if (properties.getProperty(prefix + "averagingAreaScale") != null) this.averagingAreaScale = Double.parseDouble(properties.getProperty(prefix + "averagingAreaScale")); if (properties.getProperty(prefix + "legacyMode") != null) this.legacyMode = Boolean.parseBoolean(properties.getProperty(prefix + "legacyMode")); } } /// ===== end of public static class DistortionParameters /// ============================== /* Use ROI */ /* Supply rectangle */ // Now accepts rectangles not completely contained in the image, pixels will be // copied from the image edge // getNoBayer() - replacement for splitBayer for monochrome images public double[] getNoBayer(ImagePlus imp, Rectangle r) { return getNoBayer(imp, 1, r); } // private double[][] splitBayer (ImagePlus imp, Rectangle r, boolean // equalize_greens) { public double[][] splitBayer(ImagePlus imp, Rectangle r, boolean equalize_greens) { return splitBayer(imp, 1, r, equalize_greens); } public double[] getNoBayer(ImagePlus imp, int sliceNumber, Rectangle r) { if (imp == null) return null; ImageProcessor ip = null; float[] pixels; if (imp.getStackSize() > 1) { ip = imp.getStack().getProcessor(sliceNumber); } else { ip = imp.getProcessor(); } pixels = (float[]) ip.getPixels(); int full_width = imp.getWidth(); // full image width int full_height = imp.getHeight(); // full image height if (r == null) r = new Rectangle(0, 0, full_width, full_height); double[] dpixels = new double[r.height * r.width]; int out_indx = 0; for (int y = 0; y < r.height; y++) { int y_in = y + r.y; if (y_in < 0) y_in = 0; else if (y_in >= full_height) y_in = full_height - 1; int base = y_in * full_width; for (int x = 0; x < r.width; x++) { int x_in = x + r.x; if (x_in < 0) x_in = 0; else if (x_in >= full_width) x_in = full_width - 1; dpixels[out_indx++] = pixels[base + x_in]; } } return dpixels; } public double[][] splitBayer(ImagePlus imp, int sliceNumber, Rectangle r, boolean equalize_greens) { if (imp == null) return null; ImageProcessor ip = null; float[] pixels; if (imp.getStackSize() > 1) { ip = imp.getStack().getProcessor(sliceNumber); } else { ip = imp.getProcessor(); } pixels = (float[]) ip.getPixels(); // null pointer int full_width = imp.getWidth(); // full image width int full_height = imp.getHeight(); // full image height if (r == null) r = new Rectangle(0, 0, full_width, full_height); if (debugLevel > 10) IJ.showMessage("splitBayer", "r.width=" + r.width + "\nr.height=" + r.height + "\nr.x=" + r.x + "\nr.y=" + r.y + "\nlength=" + pixels.length); if ((debugLevel > 2) && ((r.x < 0) || (r.y < 0) || ((r.x + r.width) >= full_width) || ((r.y + r.height) >= full_height))) System.out.println("r.width=" + r.width + " r.height=" + r.height + " r.x=" + r.x + " r.y=" + r.y); int x, y, base, base_b, bv, i, j; int half_height = (r.height >> 1); int half_width = (r.width >> 1); // make them all 0 if not a single pixel falls into the image int numColors = (half_height == half_width) ? 5 : 4; int pixX, pixY; double[][] bayer_pixels = new double[numColors][half_height * half_width]; if ((r.x >= full_width) || (r.y >= full_height) || ((r.x + r.width) < 0) || ((r.y + r.height) < 0)) { for (i = 0; i < bayer_pixels.length; i++) for (j = 0; j < bayer_pixels[i].length; j++) bayer_pixels[i][j] = 0.0; return bayer_pixels; } // base=r.width*((y<<1)+bv); for (y = 0; y < half_height; y++) for (bv = 0; bv < 2; bv++) { pixY = (y * 2) + bv + r.y; base_b = half_width * y; // if ((pixY>=0) if (pixY < 0) { pixY = bv; } else if (pixY >= full_height) { pixY = full_height - 2 + bv; } base = full_width * pixY + ((r.x > 0) ? r.x : 0); // base=full_width*((y*2)+bv+r.y)+r.x; pixX = r.x; if (bv == 0) for (x = 0; x < half_width; x++) { if ((pixX < 0) || (pixX >= (full_width - 2))) { bayer_pixels[0][base_b] = pixels[base]; bayer_pixels[1][base_b] = pixels[base + 1]; } else { bayer_pixels[0][base_b] = pixels[base++]; bayer_pixels[1][base_b] = pixels[base++]; } base_b++; pixX += 2; } else for (x = 0; x < half_width; x++) { if ((pixX < 0) || (pixX >= (full_width - 2))) { bayer_pixels[2][base_b] = pixels[base]; bayer_pixels[3][base_b] = pixels[base + 1]; } else { bayer_pixels[2][base_b] = pixels[base++]; bayer_pixels[3][base_b] = pixels[base++]; } base_b++; pixX += 2; } } if (equalize_greens) { double g0 = 0.0, g3 = 0.0, g02 = 0.0, g32 = 0.0, a0, a3, b0, b3; int n = bayer_pixels[0].length; for (i = 0; i < bayer_pixels[0].length; i++) { g0 += bayer_pixels[0][i]; g02 += bayer_pixels[0][i] * bayer_pixels[0][i]; g3 += bayer_pixels[3][i]; g32 += bayer_pixels[3][i] * bayer_pixels[3][i]; } g0 /= n; // mean value g3 /= n; // meran value g02 = g02 / n - g0 * g0; g32 = g32 / n - g3 * g3; b0 = Math.sqrt(Math.sqrt(g32 / g02)); b3 = 1.0 / b0; a0 = (g0 + g3) / 2 - b0 * g0; a3 = (g0 + g3) / 2 - b3 * g3; if (debugLevel > 2) { System.out.println("g0= " + g0 + ", g3= " + g3); System.out.println("g02=" + g02 + ", g32=" + g32); System.out.println("a0=" + a0 + ", b0=" + b0); System.out.println("a3=" + a3 + ", b3=" + b3); } for (i = 0; i < bayer_pixels[0].length; i++) { bayer_pixels[0][i] = a0 + bayer_pixels[0][i] * b0; bayer_pixels[3][i] = a3 + bayer_pixels[3][i] * b3; } } if (numColors > 4) bayer_pixels[4] = combineDiagonalGreens(bayer_pixels[0], bayer_pixels[3], half_width, half_height); return bayer_pixels; } public static double[][] splitBayer(ImagePlus imp, int sliceNumber, Rectangle r, boolean equalize_greens, int debug_level) { if (imp == null) return null; ImageProcessor ip = null; float[] pixels; if (imp.getStackSize() > 1) { ip = imp.getStack().getProcessor(sliceNumber); } else { ip = imp.getProcessor(); } pixels = (float[]) ip.getPixels(); // null pointer int full_width = imp.getWidth(); // full image width int full_height = imp.getHeight(); // full image height if (r == null) r = new Rectangle(0, 0, full_width, full_height); if (debug_level > 10) IJ.showMessage("splitBayer", "r.width=" + r.width + "\nr.height=" + r.height + "\nr.x=" + r.x + "\nr.y=" + r.y + "\nlength=" + pixels.length); if ((debug_level > 2) && ((r.x < 0) || (r.y < 0) || ((r.x + r.width) >= full_width) || ((r.y + r.height) >= full_height))) System.out.println("r.width=" + r.width + " r.height=" + r.height + " r.x=" + r.x + " r.y=" + r.y); int x, y, base, base_b, bv, i, j; int half_height = (r.height >> 1); int half_width = (r.width >> 1); // make them all 0 if not a single pixel falls into the image int numColors = (half_height == half_width) ? 5 : 4; int pixX, pixY; double[][] bayer_pixels = new double[numColors][half_height * half_width]; if ((r.x >= full_width) || (r.y >= full_height) || ((r.x + r.width) < 0) || ((r.y + r.height) < 0)) { for (i = 0; i < bayer_pixels.length; i++) for (j = 0; j < bayer_pixels[i].length; j++) bayer_pixels[i][j] = 0.0; return bayer_pixels; } // base=r.width*((y<<1)+bv); for (y = 0; y < half_height; y++) for (bv = 0; bv < 2; bv++) { pixY = (y * 2) + bv + r.y; base_b = half_width * y; // if ((pixY>=0) if (pixY < 0) { pixY = bv; } else if (pixY >= full_height) { pixY = full_height - 2 + bv; } base = full_width * pixY + ((r.x > 0) ? r.x : 0); // base=full_width*((y*2)+bv+r.y)+r.x; pixX = r.x; if (bv == 0) for (x = 0; x < half_width; x++) { if ((pixX < 0) || (pixX >= (full_width - 2))) { bayer_pixels[0][base_b] = pixels[base]; bayer_pixels[1][base_b] = pixels[base + 1]; } else { bayer_pixels[0][base_b] = pixels[base++]; bayer_pixels[1][base_b] = pixels[base++]; } base_b++; pixX += 2; } else for (x = 0; x < half_width; x++) { if ((pixX < 0) || (pixX >= (full_width - 2))) { bayer_pixels[2][base_b] = pixels[base]; bayer_pixels[3][base_b] = pixels[base + 1]; } else { bayer_pixels[2][base_b] = pixels[base++]; bayer_pixels[3][base_b] = pixels[base++]; } base_b++; pixX += 2; } } if (equalize_greens) { double g0 = 0.0, g3 = 0.0, g02 = 0.0, g32 = 0.0, a0, a3, b0, b3; int n = bayer_pixels[0].length; for (i = 0; i < bayer_pixels[0].length; i++) { g0 += bayer_pixels[0][i]; g02 += bayer_pixels[0][i] * bayer_pixels[0][i]; g3 += bayer_pixels[3][i]; g32 += bayer_pixels[3][i] * bayer_pixels[3][i]; } g0 /= n; // mean value g3 /= n; // meran value g02 = g02 / n - g0 * g0; g32 = g32 / n - g3 * g3; b0 = Math.sqrt(Math.sqrt(g32 / g02)); b3 = 1.0 / b0; a0 = (g0 + g3) / 2 - b0 * g0; a3 = (g0 + g3) / 2 - b3 * g3; if (debug_level > 2) { System.out.println("g0= " + g0 + ", g3= " + g3); System.out.println("g02=" + g02 + ", g32=" + g32); System.out.println("a0=" + a0 + ", b0=" + b0); System.out.println("a3=" + a3 + ", b3=" + b3); } for (i = 0; i < bayer_pixels[0].length; i++) { bayer_pixels[0][i] = a0 + bayer_pixels[0][i] * b0; bayer_pixels[3][i] = a3 + bayer_pixels[3][i] * b3; } } if (numColors > 4) bayer_pixels[4] = combineDiagonalGreens(bayer_pixels[0], bayer_pixels[3], half_width, half_height); return bayer_pixels; } //Assuming gr/bg public static double [][] simpleDemosaic( ImagePlus imp, double r2g, double b2g, double saturation, double gamma, double minlin_gamma, // do not apply gamma to lower values double hi // map to 255, gamma will preserve ) { boolean debug_this = false; double kr= 0.299; double kb = 0.114; double kg = 1.0 - kr - kb; ImageProcessor ip = null; float[] pixels; ip = imp.getProcessor(); pixels = (float[]) ip.getPixels(); // null pointer int width = imp.getWidth(); // full image width int height = imp.getHeight(); // full image height double [][] rgb = new double [3][width*height]; double [] sg= {0.0,0.0}; for (int y = 0; y < height; y+=2) { for (int x = 0; x < width; x+=2) { int indx0 = width*y+x; int indx3 = indx0+width+1; sg[0]+=pixels[indx0]; sg[1]+=pixels[indx3]; } } double g_av = Math.sqrt(sg[0] * sg[1]); double g2g0 = g_av/sg[0]; double g2g3 = g_av/sg[1]; for (int y = 0; y < height; y+=2) { for (int x = 0; x < width; x+=2) { int indx0 = width*y+x; int indx1 = indx0+1; int indx2 = indx0+width; int indx3 = indx2+1; sg[0]+=pixels[indx0]; sg[1]+=pixels[indx3]; rgb[1][indx0] = pixels[indx0] * g2g0; rgb[1][indx3] = pixels[indx3] * g2g3; rgb[0][indx1] = pixels[indx1] * r2g; rgb[2][indx2] = pixels[indx2] * b2g; } } if (debug_this) { ShowDoubleFloatArrays.showArrays(rgb, width, height, true,imp.getTitle()+"split"); } if (debug_this) { // remove System.out.println("sg="+sg[0]+", "+sg[1]+", g_av="+g_av); } // bi-linear interpolation for (int y = 0; y < height; y+=2) { for (int x = 0; x < width; x+=2) { int [][] ind = new int[4][4]; int base = (y-1)*width+(x-1); for (int i = 0; i < 4;i++) { int ie = i; if ((i==0) && (y==0)) { ie = 2; } else if ((i==3) && (y==(height - 2))) { ie = 1; } for (int j = 0; j < 4; j++) { int je = j; if ((j==0) && (x==0)) { je = 2; } else if ((j==3) && (x==(width - 2))) { je = 1; } ind[i][j] = base+ie*width+je; } } // red in top left rgb[0][ind[1][1]] = 0.5* ( rgb[0][ind[1][0]] + rgb[0][ind[1][2]]); // blue in top left rgb[2][ind[1][1]] = 0.5* ( rgb[2][ind[0][1]] + rgb[2][ind[2][1]]); // green in top right rgb[1][ind[1][2]] = 0.25*( rgb[1][ind[0][2]] + rgb[1][ind[1][1]] + rgb[1][ind[1][3]] + rgb[1][ind[2][2]]); // blue in top right rgb[2][ind[1][2]] = 0.25*( rgb[2][ind[0][1]] + rgb[2][ind[0][3]] + rgb[2][ind[2][1]] + rgb[2][ind[2][3]]); // red in bottom left rgb[0][ind[2][1]] = 0.25*( rgb[0][ind[1][0]] + rgb[0][ind[1][2]] + rgb[0][ind[3][0]] + rgb[0][ind[3][2]]); // green in bottom left rgb[1][ind[2][1]] = 0.25*( rgb[1][ind[1][1]] + rgb[1][ind[2][0]] + rgb[1][ind[2][2]] + rgb[1][ind[3][1]]); // red in bottom right rgb[0][ind[2][2]] = 0.5* ( rgb[0][ind[1][2]] + rgb[0][ind[3][2]]); // blue in bottom right rgb[2][ind[2][2]] = 0.5* ( rgb[2][ind[2][1]] + rgb[2][ind[2][3]]); } } if (debug_this) { ShowDoubleFloatArrays.showArrays(rgb, width, height, true,imp.getTitle()+"bilinear"); } if (saturation != 1.0) { for (int i = 0; i < rgb[0].length; i++) { rgb[0][i] = rgb[1][i] * Math.pow(rgb[0][i]/rgb[1][i], saturation); rgb[2][i] = rgb[1][i] * Math.pow(rgb[2][i]/rgb[1][i], saturation); } } // gamma-correction /* double gamma, double minlin_gamma, // do not apply gamma to lower values double hi // map to 255, gamma will preserve */ boolean nogamma = (gamma == 1.0); double out_range = 255.0; double lin_scale = out_range/hi; if (nogamma) { minlin_gamma = hi; } double scale_out = out_range / (Math.pow(hi, gamma) - Math.pow(minlin_gamma, gamma)*(1.0 - gamma)); // double Y0 = scale_out * Math.pow(minlin_gamma, gamma)*(1.0 - gamma); double rY0 = Math.pow(minlin_gamma, gamma)*(1.0 - gamma); if (!nogamma) { lin_scale = scale_out* gamma*Math.pow(minlin_gamma, gamma - 1); } for (int indx = 0; indx < rgb[0].length; indx++) { double Y = rgb[0][indx]*kr+rgb[1][indx]*kg+rgb[2][indx]*kb; // intensity to apply gamma double s = lin_scale; if (!nogamma && (Y > minlin_gamma)) { s = scale_out * (Math.pow(Y, gamma) - rY0)/Y; } for (int i = 0; i < 3; i++) { rgb[i][indx] *= s; } } if (debug_this) { ShowDoubleFloatArrays.showArrays(rgb, width, height, true,imp.getTitle()+"gamma"); } return rgb; } public double[][] splitBayerOne(ImagePlus imp, Rectangle r, boolean equalize_greens) { ImageProcessor ip = imp.getProcessor(); float[] pixels; pixels = (float[]) ip.getPixels(); int full_width = imp.getWidth(); // full image width int full_height = imp.getHeight(); // full image height if (debugLevel > 10) IJ.showMessage("splitBayer", "r.width=" + r.width + "\nr.height=" + r.height + "\nr.x=" + r.x + "\nr.y=" + r.y + "\nlength=" + pixels.length); if ((debugLevel > 2) && ((r.x < 0) || (r.y < 0) || ((r.x + r.width) >= full_width) || ((r.y + r.height) >= full_height))) System.out.println("r.width=" + r.width + " r.height=" + r.height + " r.x=" + r.x + " r.y=" + r.y); int x, y, base, base_b, bv, i, j; int half_height = r.height >> 1; int half_width = r.width >> 1; // make them all 0 if not a single pixel falls into the image int numColors = (half_height == half_width) ? 5 : 4; int pixX, pixY; double[][] bayer_pixels = new double[numColors][half_height * half_width]; if ((r.x >= full_width) || (r.y >= full_height) || ((r.x + r.width) < 0) || ((r.y + r.height) < 0)) { for (i = 0; i < bayer_pixels.length; i++) for (j = 0; j < bayer_pixels[i].length; j++) bayer_pixels[i][j] = 0.0; return bayer_pixels; } // base=r.width*((y<<1)+bv); for (y = 0; y < half_height; y++) for (bv = 0; bv < 2; bv++) { pixY = (y * 2) + bv + r.y; base_b = half_width * y; // if ((pixY>=0) if (pixY < 0) { pixY = bv; } else if (pixY >= full_height) { pixY = full_height - 2 + bv; } base = full_width * pixY + ((r.x > 0) ? r.x : 0); // base=full_width*((y*2)+bv+r.y)+r.x; pixX = r.x; if (bv == 0) for (x = 0; x < half_width; x++) { if ((pixX < 0) || (pixX >= (full_width - 2))) { bayer_pixels[0][base_b] = pixels[base]; bayer_pixels[1][base_b] = pixels[base + 1]; } else { bayer_pixels[0][base_b] = pixels[base++]; bayer_pixels[1][base_b] = pixels[base++]; } base_b++; pixX += 2; } else for (x = 0; x < half_width; x++) { if ((pixX < 0) || (pixX >= (full_width - 2))) { bayer_pixels[2][base_b] = pixels[base]; bayer_pixels[3][base_b] = pixels[base + 1]; } else { bayer_pixels[2][base_b] = pixels[base++]; bayer_pixels[3][base_b] = pixels[base++]; } base_b++; pixX += 2; } } if (equalize_greens) { double g0 = 0.0, g3 = 0.0, g02 = 0.0, g32 = 0.0, a0, a3, b0, b3; int n = bayer_pixels[0].length; for (i = 0; i < bayer_pixels[0].length; i++) { g0 += bayer_pixels[0][i]; g02 += bayer_pixels[0][i] * bayer_pixels[0][i]; g3 += bayer_pixels[3][i]; g32 += bayer_pixels[3][i] * bayer_pixels[3][i]; } g0 /= n; // mean value g3 /= n; // meran value g02 = g02 / n - g0 * g0; g32 = g32 / n - g3 * g3; b0 = Math.sqrt(Math.sqrt(g32 / g02)); b3 = 1.0 / b0; a0 = (g0 + g3) / 2 - b0 * g0; a3 = (g0 + g3) / 2 - b3 * g3; if (debugLevel > 2) { System.out.println("g0= " + g0 + ", g3= " + g3); System.out.println("g02=" + g02 + ", g32=" + g32); System.out.println("a0=" + a0 + ", b0=" + b0); System.out.println("a3=" + a3 + ", b3=" + b3); } for (i = 0; i < bayer_pixels[0].length; i++) { bayer_pixels[0][i] = a0 + bayer_pixels[0][i] * b0; bayer_pixels[3][i] = a3 + bayer_pixels[3][i] * b3; } } if (numColors > 4) bayer_pixels[4] = combineDiagonalGreens(bayer_pixels[0], bayer_pixels[3], half_width, half_height); return bayer_pixels; } public double[][] splitBayerZero(ImagePlus imp, Rectangle r, boolean equalize_greens) { ImageProcessor ip = imp.getProcessor(); float[] pixels; pixels = (float[]) ip.getPixels(); if (debugLevel > 10) IJ.showMessage("splitBayer", "r.width=" + r.width + "\nr.height=" + r.height + "\nr.x=" + r.x + "\nr.y=" + r.y + "\nlength=" + pixels.length); int x, y, base, base_b, bv, i; int half_height = r.height >> 1; int half_width = r.width >> 1; int full_width = imp.getWidth(); // full image width int full_height = imp.getHeight(); // full image height int numColors = (half_height == half_width) ? 5 : 4; int pixX, pixY; double[][] bayer_pixels = new double[numColors][half_height * half_width]; // base=r.width*((y<<1)+bv); for (y = 0; y < half_height; y++) for (bv = 0; bv < 2; bv++) { pixY = (y * 2) + bv + r.y; base_b = half_width * y; if ((pixY < 0) || (pixY >= full_height)) { if (bv == 0) for (x = 0; x < half_width; x++) { bayer_pixels[0][base_b] = 0.0; bayer_pixels[1][base_b] = 0.0; base_b++; } else for (x = 0; x < half_width; x++) { bayer_pixels[2][base_b] = 0.0; bayer_pixels[3][base_b] = 0.0; base_b++; } } else { base = full_width * ((y * 2) + bv + r.y) + r.x; pixX = r.x; if (bv == 0) for (x = 0; x < half_width; x++) { if ((pixX < 0) || (pixX >= (full_width - 1))) { bayer_pixels[0][base_b] = 0.0; bayer_pixels[1][base_b] = 0.0; base += 2; } else { bayer_pixels[0][base_b] = pixels[base++]; bayer_pixels[1][base_b] = pixels[base++]; } base_b++; pixX += 2; } else for (x = 0; x < half_width; x++) { if ((pixX < 0) || (pixX >= (full_width - 1))) { bayer_pixels[2][base_b] = 0.0; bayer_pixels[3][base_b] = 0.0; base += 2; } else { bayer_pixels[2][base_b] = pixels[base++]; bayer_pixels[3][base_b] = pixels[base++]; } base_b++; pixX += 2; } } } if (equalize_greens) { double g0 = 0.0, g3 = 0.0, g02 = 0.0, g32 = 0.0, a0, a3, b0, b3; int n = bayer_pixels[0].length; for (i = 0; i < bayer_pixels[0].length; i++) { g0 += bayer_pixels[0][i]; g02 += bayer_pixels[0][i] * bayer_pixels[0][i]; g3 += bayer_pixels[3][i]; g32 += bayer_pixels[3][i] * bayer_pixels[3][i]; } g0 /= n; // mean value g3 /= n; // meran value g02 = g02 / n - g0 * g0; g32 = g32 / n - g3 * g3; b0 = Math.sqrt(Math.sqrt(g32 / g02)); b3 = 1.0 / b0; a0 = (g0 + g3) / 2 - b0 * g0; a3 = (g0 + g3) / 2 - b3 * g3; if (debugLevel > 2) { System.out.println("g0= " + g0 + ", g3= " + g3); System.out.println("g02=" + g02 + ", g32=" + g32); System.out.println("a0=" + a0 + ", b0=" + b0); System.out.println("a3=" + a3 + ", b3=" + b3); } for (i = 0; i < bayer_pixels[0].length; i++) { bayer_pixels[0][i] = a0 + bayer_pixels[0][i] * b0; bayer_pixels[3][i] = a3 + bayer_pixels[3][i] * b3; } } if (numColors > 4) bayer_pixels[4] = combineDiagonalGreens(bayer_pixels[0], bayer_pixels[3], half_width, half_height); return bayer_pixels; } /* * Create a Thread[] array as large as the number of processors available. From * Stephan Preibisch's Multithreading.java class. See: * http://repo.or.cz/w/trakem2.git?a=blob;f=mpi/fruitfly/general/MultiThreading. * java;hb=HEAD */ private Thread[] newThreadArray(int maxCPUs) { int n_cpus = Runtime.getRuntime().availableProcessors(); if (n_cpus > maxCPUs) n_cpus = maxCPUs; return new Thread[n_cpus]; } /* * Start all given threads and wait on each of them until all are done. From * Stephan Preibisch's Multithreading.java class. See: * http://repo.or.cz/w/trakem2.git?a=blob;f=mpi/fruitfly/general/MultiThreading. * java;hb=HEAD */ private static void startAndJoin(Thread[] threads) { for (int ithread = 0; ithread < threads.length; ++ithread) { threads[ithread].setPriority(Thread.NORM_PRIORITY); threads[ithread].start(); } try { for (int ithread = 0; ithread < threads.length; ++ithread) threads[ithread].join(); } catch (InterruptedException ie) { throw new RuntimeException(ie); } } // Parameters for identifying red laser pointers on the image of the pattern // grid }