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