Commit da579ebb authored by Andrey Filippov's avatar Andrey Filippov

Enhancing moving targets

parent 4e5e1f22
package com.elphel.imagej.common; package com.elphel.imagej.common;
import java.awt.Rectangle; import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
...@@ -216,6 +217,111 @@ import ij.process.ImageProcessor; ...@@ -216,6 +217,111 @@ import ij.process.ImageProcessor;
show); // boolean show show); // boolean show
} }
public static ImagePlus showArraysHyperstack(
float[][][] pixels,
int width,
String title,
String [] titles, // all slices*frames titles or just slice titles or null
String [] frame_titles, // frame titles or null
boolean show) {
int num_frames = pixels.length;
int num_slices = pixels[0].length;
float [][] fpixels = new float [num_frames*num_slices][];
for (int f = 0; f < num_frames; f++) {
for (int s = 0; s < num_slices; s ++) {
int indx = s + f * num_slices;
fpixels[indx] = pixels[f][s];
}
}
String [] combo_titles;
if ((titles != null) && (titles.length == fpixels.length)) {
combo_titles = titles;
} else {
combo_titles = new String[fpixels.length];
if (titles == null) {
titles = new String [pixels[0].length];
for (int i = 0; i < titles.length; i++) {
titles[i] = ""+i;
}
}
if (frame_titles == null) {
frame_titles = new String [pixels.length];
for (int i = 0; i < titles.length; i++) {
frame_titles[i] = ""+i;
}
}
for (int f = 0; f < frame_titles.length; f++) {
for (int s = 0; s < titles.length; s++) {
combo_titles[s + f * titles.length] = frame_titles[f]+":"+titles[s];
}
}
}
int height = -1;
for (int slic = 0; slic < fpixels.length; slic++) {
if (fpixels[slic] != null) {
height = fpixels[slic].length/width;
break;
}
}
return showArraysHyperstack(
fpixels, // double[][] pixels,
width, // int width,
height, // int height,
pixels[0].length, // int slices,
title, // String title,
combo_titles, // String [] titles);
show); // boolean show
}
public static ImagePlus showArraysHyperstack(
float[][] pixels,
int width,
int height,
int slices,
String title,
String [] titles,
boolean show) {
int i,j;
if (pixels==null) {
System.out.println("showDoubleFloatArrays.showArrays(): - pixel array is null");
}
float [] fpixels;
ImageStack array_stack=new ImageStack(width,height);
boolean not_empty = false;
for (i=0;i<pixels.length;i++) if (pixels[i]!=null) {
not_empty = true;
fpixels=new float[pixels[i].length];
for (j=0;j<fpixels.length;j++) fpixels[j]=(float)pixels[i][j];
if (i < titles.length) {
array_stack.addSlice(titles[i], fpixels);
} else {
array_stack.addSlice("slice-"+i, fpixels);
}
}
ImagePlus imp_stack = null;
if (not_empty) {
imp_stack = IJ.createImage(
title, // String title,
"32-bit,grayscale-mode", // ,label", // String type,
width, // int width,
height, // int height,
1, // int channels,
slices, // int slices,
pixels.length / slices); // frames)
imp_stack.setStack(array_stack);
imp_stack.getProcessor().resetMinAndMax();
if (show) {
imp_stack.show();
}
}
return imp_stack;
}
public static void showComplex(double[][][] cpixels, String title) { public static void showComplex(double[][][] cpixels, String title) {
...@@ -857,6 +963,123 @@ G= Y +Pr*(- 2*Kr*(1-Kr))/Kg + Pb*(-2*Kb*(1-Kb))/Kg ...@@ -857,6 +963,123 @@ G= Y +Pr*(- 2*Kr*(1-Kr))/Kg + Pb*(-2*Kb*(1-Kb))/Kg
return imp; return imp;
} }
public static double [][] readDoubleArray(
ImagePlus imp,
int num_slices, // (0 - all)
int [] wh) {
float [][] fdata = readFloatArray(
imp, // ImagePlus imp,
num_slices, // int num_slices, // (0 - all)
wh); // int [] wh)
if (fdata == null) {
return null;
}
double [][] result = new double [fdata.length][];
for (int n = 0; n < fdata.length; n++) if (fdata[n]!=null) {
result[n] = new double [fdata[n].length];
for (int i = 0; i < fdata[n].length; i++) {
result[n][i] = fdata[n][i];
}
}
return result;
}
public static float [][] readFloatArray(
ImagePlus imp,
int num_slices, // (0 - all)
int [] wh) {
if ((imp == null) || (imp.getTitle() == null) || (imp.getTitle().equals(""))) {
return null;
}
ImageStack imageStack = imp.getStack();
int nChn=imageStack.getSize();
if ((num_slices > 0) && (num_slices < nChn)) {
nChn = num_slices;
}
float [][] result = new float [nChn][];
for (int n = 0; n < nChn; n++) {
result[n] = (float[]) imageStack.getPixels(n + 1);
}
if (wh != null) {
wh[0] = imp.getWidth();
wh[1] = imp.getHeight();
}
return result;
}
public static float [][] readFloatArray(
String file_path,
int num_slices, // (0 - all)
int [] wh) {
ImagePlus imp = null;
try {
imp = new ImagePlus(file_path);
} catch (Exception e) {
System.out.println ("Failed to open "+file_path+", maybe will generate it");
}
if ((imp == null) || (imp.getTitle() == null) || (imp.getTitle().equals(""))) {
return null;
}
System.out.println ("Read "+(imp.getStack().getSize())+" slices from "+file_path);
return readFloatArray(
imp, // ImagePlus imp,
num_slices, //int num_slices, // (0 - all)
wh); // int [] wh)
}
public static double [][][] readDoubleHyperstack(
String path,
int [] wh, // should be null or int[2]
String [][] ptop_titles, // should be null or String [1][]
String [][] pslice_titles){// should be null or String [1][]
ImagePlus imp = new ImagePlus(path);
if (imp.getWidth() == 0) {
System.out.println("testSynthetic(): Failed reading Vector field from: " + path);
return null;
}
int num_slices = imp.getStackSize();
String [] slice_labels = imp.getStack().getSliceLabels();
ArrayList<String> top_titles = new ArrayList<String>();
ArrayList<String> slice_titles = new ArrayList<String>();
for (int i = 0; i < num_slices; i++) {
String [] label = slice_labels[i].split(":");
if (label.length>1) {
if (!top_titles.contains(label[0])) {
top_titles.add(label[0]);
}
if (!slice_titles.contains(label[1])) {
slice_titles.add(label[1]);
}
} else if (label.length>0) {
if (!slice_titles.contains(label[0])) {
slice_titles.add(label[0]);
}
}
}
String [] top_l = top_titles.toArray(new String[0]);
String [] slice_l = slice_titles.toArray(new String[0]);
if (ptop_titles != null) {
ptop_titles[0] = top_l;
}
if (pslice_titles != null) {
pslice_titles[0] = slice_l;
}
double [][] file_data = ShowDoubleFloatArrays.readDoubleArray(
imp, // ImagePlus imp,
0, // int num_slices, // (0 - all)
wh); // int [] wh); // int [] wh)
int num_top_slices = num_slices/slice_titles.size();
double [][][] out_data = new double [num_top_slices][num_slices/num_top_slices][];
for (int t = 0; t < out_data.length; t++) {
for (int s = 0; s < out_data[t].length; s++) {
out_data[t][s] = file_data[t * out_data[t].length + s];
}
}
return out_data;
}
} }
...@@ -109,6 +109,7 @@ import com.elphel.imagej.common.DoubleGaussianBlur; ...@@ -109,6 +109,7 @@ import com.elphel.imagej.common.DoubleGaussianBlur;
import com.elphel.imagej.common.GenericJTabbedDialog; import com.elphel.imagej.common.GenericJTabbedDialog;
import com.elphel.imagej.common.ShowDoubleFloatArrays; import com.elphel.imagej.common.ShowDoubleFloatArrays;
import com.elphel.imagej.common.WindowTools; import com.elphel.imagej.common.WindowTools;
import com.elphel.imagej.cuas.CuasMotion;
import com.elphel.imagej.dct.FactorConvKernel; import com.elphel.imagej.dct.FactorConvKernel;
import com.elphel.imagej.gpu.GPUTileProcessor; import com.elphel.imagej.gpu.GPUTileProcessor;
import com.elphel.imagej.gpu.GpuQuad; import com.elphel.imagej.gpu.GpuQuad;
...@@ -905,6 +906,8 @@ public class Eyesis_Correction implements PlugIn, ActionListener { ...@@ -905,6 +906,8 @@ public class Eyesis_Correction implements PlugIn, ActionListener {
addJButton("UAS log", jpanelOrange, color_process); addJButton("UAS log", jpanelOrange, color_process);
addJButton("DJI SRT", jpanelOrange, color_process); addJButton("DJI SRT", jpanelOrange, color_process);
addJButton("SRT to KML", jpanelOrange, color_process); addJButton("SRT to KML", jpanelOrange, color_process);
addJButton("Motion_CUAS", jpanelOrange, color_stop);
addJButton("Motion_scan", jpanelOrange, color_stop);
// //
plugInJFrame.add(jpanelOrange); plugInJFrame.add(jpanelOrange);
} }
...@@ -1484,6 +1487,7 @@ public class Eyesis_Correction implements PlugIn, ActionListener { ...@@ -1484,6 +1487,7 @@ public class Eyesis_Correction implements PlugIn, ActionListener {
} }
b.addActionListener(this); b.addActionListener(this);
b.addKeyListener(IJ.getInstance()); b.addKeyListener(IJ.getInstance());
b.setToolTipText(label);
panel.add(b); panel.add(b);
} }
...@@ -6285,6 +6289,26 @@ public class Eyesis_Correction implements PlugIn, ActionListener { ...@@ -6285,6 +6289,26 @@ public class Eyesis_Correction implements PlugIn, ActionListener {
testDjiSrt(); testDjiSrt();
} else if (label.equals("SRT to KML")) { } else if (label.equals("SRT to KML")) {
djiSrtToKml(); djiSrtToKml();
} else if (label.equals("Motion_CUAS")) {
DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
ImagePlus imp_sel = WindowManager.getCurrentImage();
if (imp_sel != null) {
motion_cuas(
imp_sel,
0); // int mode));
} else {
System.out.println("No image selected");
}
} else if (label.equals("Motion_scan")) {
DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
ImagePlus imp_sel = WindowManager.getCurrentImage();
if (imp_sel != null) {
motion_cuas(
imp_sel,
1); // int mode));
} else {
System.out.println("No image selected");
}
} }
} }
...@@ -6329,6 +6353,77 @@ public class Eyesis_Correction implements PlugIn, ActionListener { ...@@ -6329,6 +6353,77 @@ public class Eyesis_Correction implements PlugIn, ActionListener {
return true; return true;
} }
public static boolean motion_cuas(
ImagePlus imp_sel,
int mode) {
// long startTime = System.nanoTime();
// load needed sensor and kernels files
setAllProperties(PROPERTIES); // batchRig may save properties with the model. Extrinsics will be updated,
if (GPU_TILE_PROCESSOR == null) {
try {
GPU_TILE_PROCESSOR = new GPUTileProcessor(CORRECTION_PARAMETERS.tile_processor_gpu);
} catch (Exception e) {
System.out.println("Failed to initialize GPU class");
// TODO Auto-generated catch block
e.printStackTrace();
return false;
} // final int debugLevel);
}
EYESIS_CORRECTIONS_AUX.initSensorFiles(DEBUG_LEVEL);
if (!QUAD_CLT_AUX.CLTKernelsAvailable()) {
if (DEBUG_LEVEL > 0) {
System.out.println("Reading AUX CLT kernels");
}
QUAD_CLT_AUX.readCLTKernels(CLT_PARAMETERS, THREADS_MAX, UPDATE_STATUS, // update status info
DEBUG_LEVEL);
if (DEBUG_LEVEL > 1) {
QUAD_CLT_AUX.showCLTKernels(THREADS_MAX, UPDATE_STATUS, // update status info
DEBUG_LEVEL);
}
}
if (!QUAD_CLT_AUX.geometryCorrectionAvailable()) {
if (DEBUG_LEVEL > 0) {
System.out.println("Calculating geometryCorrection");
}
if (!QUAD_CLT_AUX.initGeometryCorrection(DEBUG_LEVEL + 2)) {
return false;
}
}
if (CLT_PARAMETERS.useGPU(true) && (QUAD_CLT_AUX != null) && (GPU_QUAD_AUX == null)) { // if GPU AUX is
// needed
try {
GPU_QUAD_AUX = new GpuQuad(//
GPU_TILE_PROCESSOR, QUAD_CLT_AUX, CLT_PARAMETERS.gpu_debug_level);
} catch (Exception e) {
System.out.println("Failed to initialize GpuQuad class");
// TODO Auto-generated catch block
e.printStackTrace();
return false;
} // final int debugLevel);
QUAD_CLT_AUX.setGPU(GPU_QUAD_AUX);
}
switch (mode) {
case 0:
CuasMotion.testCuasMotion(
imp_sel, // ImagePlus imp_sel,
CLT_PARAMETERS, // CLTParameters clt_parameters,
QUAD_CLT_AUX, // QuadCLT parentCLT)
DEBUG_LEVEL); // //int debugLevel)
break;
case 1:
CuasMotion.testCuasScanMotion(
imp_sel, // ImagePlus imp_sel,
CLT_PARAMETERS, // CLTParameters clt_parameters,
QUAD_CLT_AUX, // QuadCLT parentCLT)
DEBUG_LEVEL); // //int debugLevel)
break;
}
return true;
}
public static boolean djiSrtToKml() { public static boolean djiSrtToKml() {
String srt_path= "/home/elphel/Documents/dji/images/DJI_001-02/DJI_20250515122524_0007_D.SRT"; String srt_path= "/home/elphel/Documents/dji/images/DJI_001-02/DJI_20250515122524_0007_D.SRT";
String kml_path= "/home/elphel/Documents/dji/images/DJI_001-02/DJI_20250515122524_0007_D.kml"; String kml_path= "/home/elphel/Documents/dji/images/DJI_001-02/DJI_20250515122524_0007_D.kml";
......
...@@ -492,7 +492,7 @@ public class CorrectionFPN { ...@@ -492,7 +492,7 @@ public class CorrectionFPN {
return null; return null;
} }
ImagejJp4Tiff.decodeProperiesFromInfo(imp); ImagejJp4Tiff.decodeProperiesFromInfo(imp);
double [][] rowcol_data2 = QuadCLT.readDoubleArray( double [][] rowcol_data2 = ShowDoubleFloatArrays.readDoubleArray(
imp, // ImagePlus imp, imp, // ImagePlus imp,
0, // int num_slices, // (0 - all) 0, // int num_slices, // (0 - all)
wh); // int [] wh); // int [] wh) wh); // int [] wh); // int [] wh)
......
...@@ -123,7 +123,7 @@ public class CuasData implements Serializable { ...@@ -123,7 +123,7 @@ public class CuasData implements Serializable {
int [] wh = new int[2]; int [] wh = new int[2];
float [][] fclt_w; float [][] fclt_w;
String cuas_old_path = full_path+Prefs.getFileSeparator() + name+ QuadCLTCPU.CENTER_CLT_SUFFIX+ ".tiff"; String cuas_old_path = full_path+Prefs.getFileSeparator() + name+ QuadCLTCPU.CENTER_CLT_SUFFIX+ ".tiff";
fclt_w = QuadCLT.readFloatArray( fclt_w = ShowDoubleFloatArrays.readFloatArray(
cuas_old_path, // String file_path, cuas_old_path, // String file_path,
0, // int num_slices, // (0 - all) 0, // int num_slices, // (0 - all)
wh); // int [] wh) { wh); // int [] wh) {
......
package com.elphel.imagej.cuas;
import java.awt.Rectangle;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;
import com.elphel.imagej.cameras.CLTParameters;
import com.elphel.imagej.common.GenericJTabbedDialog;
import com.elphel.imagej.common.ShowDoubleFloatArrays;
import com.elphel.imagej.gpu.GPUTileProcessor;
import com.elphel.imagej.gpu.GpuQuad;
import com.elphel.imagej.gpu.TpTask;
import com.elphel.imagej.tileprocessor.ImageDtt;
import com.elphel.imagej.tileprocessor.QuadCLT;
import com.elphel.imagej.tileprocessor.TDCorrTile;
import com.elphel.imagej.tileprocessor.TileNeibs;
import ij.ImagePlus;
public class CuasMotion {
final static private int INDX_VX = 0;
final static private int INDX_VY = 1;
final static private int INDX_STRENGTH = 2;
final static private int INDX_FRAC = 3;
private final GPUTileProcessor gpuTileProcessor;
private CLTParameters clt_parameters=null;
private QuadCLT parentCLT = null;
private int debugLevel = 0;
private GpuQuad gpuQuad = null;
private ImageDtt image_dtt = null;
private int gpu_max_width;
private int gpu_max_height;
private int tilesX;
private int tilesY;
public CuasMotion (
CLTParameters clt_parameters,
QuadCLT parentCLT,
int debugLevel) {
this.debugLevel = debugLevel;
this.clt_parameters = clt_parameters;
this.parentCLT = parentCLT;
System.out.println("Setting up GPU");
gpuTileProcessor = parentCLT.getGPUQuad().getGpuTileProcessor();
tilesX = parentCLT.getTilesX();
tilesY = parentCLT.getTilesY();
gpu_max_width = tilesX * GPUTileProcessor.DTT_SIZE;
gpu_max_height = tilesY * GPUTileProcessor.DTT_SIZE;
try {
gpuQuad = new GpuQuad(//single camera
gpuTileProcessor, // GPUTileProcessor gpuTileProcessor,
gpu_max_width, // final int max_width,
gpu_max_height, // final int max_height,
1, // final int num_colors, // normally 1?
this.clt_parameters.gpu_debug_level);
} catch (Exception e) {
System.out.println("Failed to initialize GpuQuad class");
// TODO Auto-generated catch block
e.printStackTrace();
return;
} // final int debugLevel);
boolean is_aux = true;
boolean is_mono = true;
boolean is_lwir = true;
image_dtt = new ImageDtt(
1, // int numSensors,
GPUTileProcessor.DTT_SIZE, // clt_parameters.transform_size,
clt_parameters.img_dtt,
is_aux, // ref_scene.isAux(),
is_mono, // ref_scene.isMonochrome(),
is_lwir,
clt_parameters.getScaleStrength(is_aux), // maybe something else
gpuQuad);
}
public static boolean testCuasMotion(
ImagePlus imp_sel,
CLTParameters clt_parameters,
QuadCLT parentCLT,
int debugLevel) {
int framecent = 180;
int corr_offset = clt_parameters.imp.cuas_corr_offset;
int corr_pairs = clt_parameters.imp.cuas_corr_pairs;
// int frame0cent = 210;
// int frame1cent = frame0cent + clt_parameters.imp.cuas_corr_offset;
// int frame_len = clt_parameters.imp.cuas_corr_pairs; // 40;
double cuas_fat_zero = clt_parameters.imp.cuas_fat_zero;
double cuas_cent_radius = clt_parameters.imp.cuas_cent_radius;
int cuas_n_recenter = clt_parameters.imp.cuas_n_recenter;
double cuas_rstr = clt_parameters.imp.cuas_rstr;// 0.003; // clt_parameters.imp.rln_sngl_rstr; // FIXME: ADD
boolean smooth = clt_parameters.imp.cuas_smooth; // true;
// double rln_neib_rstr = clt_parameters.imp.rln_neib_rstr;
GenericJTabbedDialog gd = new GenericJTabbedDialog("Motion detect parameters");
gd.addNumericField("Center frame", framecent, 0, 5, "frame", "Center frame");
gd.addNumericField("Number of pairs", corr_pairs, 0, 3, "", "The number of correlation pairs to accumulate.");
gd.addNumericField("Pairs offset", corr_offset, 0, 3, "", "Offset between the correlation pairs");
gd.addCheckbox ("Smooth weights", smooth, "Apply cosine weights when averaging a sequence of correlation pairs.");
gd.addNumericField("Fat zero", cuas_fat_zero, 7, 10, "", "Fat zero for TD->PD conversion");
gd.addNumericField("Center radius", cuas_cent_radius, 3, 5, "pix", "Center radius");
gd.addNumericField("Number of recenters", cuas_n_recenter, 0, 5, "", "Number of centroid re-center iterations");
gd.addNumericField("Relative strength", cuas_rstr, 3, 5, "x", "Minimal relative strength of the 2D correlation");
gd.showDialog();
if (gd.wasCanceled())
return false;
framecent = (int) gd.getNextNumber();
corr_pairs = (int) gd.getNextNumber();
corr_offset = (int) gd.getNextNumber();
smooth = gd.getNextBoolean();
cuas_fat_zero = gd.getNextNumber();
cuas_cent_radius = gd.getNextNumber();
cuas_n_recenter = (int) gd.getNextNumber();
cuas_rstr = gd.getNextNumber();
final boolean batch_mode = false;
int frame0 = framecent - (corr_offset + corr_pairs)/2;
int frame1 = framecent + (corr_offset - corr_pairs)/2;
String suffix_param = "-"+framecent+"-"+corr_offset+"-"+corr_pairs;
String dbg_suffix = imp_sel.getTitle()+suffix_param;
if (parentCLT.getTileProcessor() == null) {
parentCLT.setTiles (imp_sel, // set global tp.tilesX, tp.tilesY
parentCLT.getNumSensors(), // tp.getNumSensors(),
clt_parameters,
QuadCLT.THREADS_MAX); // where to get it? Use instance member
}
CuasMotion cuasMotion = new CuasMotion (
clt_parameters, // CLTParameters clt_parameters,
parentCLT, // QuadCLT parentCLT,
debugLevel); // int debugLevel)
int num_slices = imp_sel.getStack().getSize();
float [][] fpixels = new float[num_slices][];
for (int nslice = 0; nslice < fpixels.length; nslice++) {
fpixels[nslice] = (float[]) imp_sel.getStack().getPixels(nslice+1);
}
TDCorrTile [] tdCorrTiles = cuasMotion.correlatePairs(
clt_parameters, // CLTParameters clt_parameters,
fpixels, // float [][] fpixels,
frame0, // int frame0,
frame1, // int frame1,
corr_pairs, // int frame_len,
smooth, // boolean smooth, // use cosine mask
true, // batch_mode, // final boolean batch_mode,
dbg_suffix, // final String dbg_suffix, // for image_names
debugLevel); // int debugLevel)
// convert to pixel domain and display
double [][] corr_tiles_pd = cuasMotion.convertTDtoPD(
tdCorrTiles, // final TDCorrTile [] tiles,
0xFE, // final int corr_type, // 0xFE
cuas_fat_zero, // final double gpu_fat_zero,
debugLevel); // final int debug_level
double [][] vector_field = TDCorrTile.getMismatchVector( // full tiles in gpu (512*512)
corr_tiles_pd, // final double[][] tiles,
cuas_rstr, // double rmax,
cuas_cent_radius, // final double centroid_radius, // 0 - all same weight, > 0 cosine(PI/2*sqrt(dx^2+dy^2)/rad)
cuas_n_recenter, // final int n_recenter); // re-center window around new maximum. 0 -no refines (single-pass)
true); // final boolean calc_fraction ){ // calculate fraction inside center circle
final int corr_size = 2 * GPUTileProcessor.DTT_SIZE -1;
boolean show_vector_field = true; // (debugLevel>100); // true;
boolean show_2d_correlations = true; // (debugLevel>0); // true;
if (show_2d_correlations) {
double [][] dbg_2d_corrs = ImageDtt.corr_partial_dbg( // not used in lwir
new double [][][] {corr_tiles_pd}, // final double [][][] corr_data, // [layer][tile][(2*transform_size-1)*(2*transform_size-1)] // if null - will not calculate
cuasMotion.tilesX, // final int tilesX,
corr_size, //final int corr_size, // 15
clt_parameters.corr_border_contrast, // final double border_contrast,
debugLevel); // final int globalDebugLevel)
String [] dbg_titles= {"raw"}; // ,"neibs"};
ShowDoubleFloatArrays.showArrays(
dbg_2d_corrs,
cuasMotion.tilesX * (corr_size + 1),
cuasMotion.tilesY * (corr_size + 1),
true,
imp_sel.getTitle()+"-corr2d"+suffix_param, // "-corr2d"+"-"+frame0+"-"+frame1+"-"+corr_pairs,
dbg_titles);
}
if (show_vector_field) {
// double [][] dbg_vf = new double [3 * vector_field.length][cuasMotion.tilesX * cuasMotion.tilesY];
double [][] dbg_vf = new double [4][cuasMotion.tilesX * cuasMotion.tilesY];
String [] dbg_titles = new String[dbg_vf.length];
dbg_titles [0] = "vX";
dbg_titles [1] = "vY";
dbg_titles [2] = "conf";
dbg_titles [3] = "frac";
for (int i = 0; i < dbg_vf.length; i++) {
Arrays.fill(dbg_vf[i], Double.NaN);
}
for (int t=0; t<dbg_vf[0].length; t++) {
if (vector_field[t] != null) {
for (int k = 0; k < dbg_titles.length; k++) {
dbg_vf[k][t] = vector_field[t][k];
}
}
}
ShowDoubleFloatArrays.showArrays(
dbg_vf,
cuasMotion.tilesX,
cuasMotion.tilesY,
true,
imp_sel.getTitle()+"-vector_field"+suffix_param,
dbg_titles);
}
// return vector_field; // corr_tiles;
return true;
}
public static boolean testCuasScanMotion(
ImagePlus imp_sel,
CLTParameters clt_parameters,
QuadCLT parentCLT,
int debugLevel) {
int corr_offset = clt_parameters.imp.cuas_corr_offset;
int corr_pairs = clt_parameters.imp.cuas_corr_pairs;
double fat_zero = clt_parameters.imp.cuas_fat_zero;
double cent_radius = clt_parameters.imp.cuas_cent_radius;
int n_recenter = clt_parameters.imp.cuas_n_recenter;
double rstr = clt_parameters.imp.cuas_rstr;// 0.003; // clt_parameters.imp.rln_sngl_rstr; // FIXME: ADD
double speed_min = clt_parameters.imp.cuas_speed_min;
double speed_pref = clt_parameters.imp.cuas_speed_pref;
double speed_boost = clt_parameters.imp.cuas_speed_boost;
boolean smooth = clt_parameters.imp.cuas_smooth; // true;
boolean half_step = clt_parameters.imp.cuas_half_step; // true;
int max_range = clt_parameters.imp.cuas_max_range;
String vf_extended_dir = "/media/elphel/NVME/lwir16-proc/eagle_mountain/linked/movement/selected/13";
//file:///media/elphel/NVME/lwir16-proc/eagle_mountain/linked/movement/selected/12/INPUT-1747803449_165687.tiff-vector_extended-offs20-pairs50-rstr0.01-fz300.0-cr3.0-mr2-ms0.3-sp1.0-2.5.tiff
// gd.addStringField("KML document title", name, 100, "Provide title of the KML file");
// srt_path = gd.getNextString();
boolean save_params = true;
while (true) {
GenericJTabbedDialog gd = new GenericJTabbedDialog("Motion scan parameters");
// gd.addNumericField("Center frame", framecent, 0, 5, "frame", "Center frame");
gd.addNumericField("Number of pairs", corr_pairs, 0, 3, "", "The number of correlation pairs to accumulate.");
gd.addNumericField("Pairs offset", corr_offset, 0, 3, "", "Offset between the correlation pairs");
gd.addCheckbox ("Smooth weights", smooth, "Apply cosine weights when averaging a sequence of correlation pairs.");
gd.addCheckbox ("Half scan step", half_step, "Reduce step for motion detection = offset/2, if false = offset.");
gd.addNumericField("Fat zero", fat_zero, 7, 10, "", "Fat zero for TD->PD conversion");
gd.addNumericField("Center radius", cent_radius, 3, 5, "pix", "Center radius");
gd.addNumericField("Number of recenters", n_recenter, 0, 5, "", "Number of centroid re-center iterations");
gd.addNumericField("Relative strength", rstr, 3, 5, "x", "Minimal absoute strength of the 2D correlation (negative - relative)");
gd.addNumericField("Minimal speed", speed_min, 5,8,"ppr", "Minimal target speed in pixels per range (per cuas_corr_offset).");
gd.addNumericField("Preferable speed", speed_pref, 5,8,"ppr", "Boost effective strength when speed is above this.");
gd.addNumericField("Maximal speed boost", speed_boost, 5,8,"", "Maximal speed-caused effective strength boost.");
gd.addNumericField("Local max range", max_range, 0,3,"", "While filtering local correlation maximums: 1 - 3x3 neighbors, 2 - 5x5 ones.");
gd.addStringField("vf_extended_dir",vf_extended_dir,100,"directory to read vf_extended file");
gd.addCheckbox ("Save_params", save_params, "Save edited parameters");
gd.showDialog();
if (gd.wasCanceled()) {
return false;
}
corr_pairs = (int) gd.getNextNumber();
corr_offset = (int) gd.getNextNumber();
smooth = gd.getNextBoolean();
half_step = gd.getNextBoolean();
fat_zero = gd.getNextNumber();
cent_radius = gd.getNextNumber();
n_recenter = (int) gd.getNextNumber();
rstr = gd.getNextNumber();
speed_min = gd.getNextNumber();
speed_pref = gd.getNextNumber();
speed_boost = gd.getNextNumber();
max_range = (int) gd.getNextNumber();
vf_extended_dir = gd.getNextString();
save_params = gd.getNextBoolean();
if (save_params) {
clt_parameters.imp.cuas_corr_offset = corr_offset;
clt_parameters.imp.cuas_corr_pairs = corr_pairs;
clt_parameters.imp.cuas_fat_zero = fat_zero;
clt_parameters.imp.cuas_cent_radius = cent_radius;
clt_parameters.imp.cuas_n_recenter = n_recenter;
clt_parameters.imp.cuas_rstr = rstr;
clt_parameters.imp.cuas_speed_min = speed_min;
clt_parameters.imp.cuas_speed_pref = speed_pref;
clt_parameters.imp.cuas_speed_boost = speed_boost;
clt_parameters.imp.cuas_smooth = smooth;
clt_parameters.imp.cuas_max_range = max_range;
clt_parameters.imp.cuas_half_step = half_step;
}
int start_frame = 0;
if (parentCLT.getTileProcessor() == null) {
parentCLT.setTiles (imp_sel, // set global tp.tilesX, tp.tilesY
parentCLT.getNumSensors(), // tp.getNumSensors(),
clt_parameters,
QuadCLT.THREADS_MAX); // where to get it? Use instance member
}
int first_corr = 1; // skip average
int num_scenes = imp_sel.getStack().getSize()- first_corr; // includes average
int seq_length = corr_offset + corr_pairs;
int corr_step = half_step ? (corr_offset/2) : corr_offset;
int num_corr_samples = (num_scenes - seq_length - start_frame) / corr_step;
if (debugLevel > -4) {
System.out.println("corr_pairs= "+corr_pairs);
System.out.println("corr_offset= "+corr_offset);
System.out.println("seq_length= "+seq_length);
System.out.println("corr_step= "+corr_step);
System.out.println("num_scenes= "+num_scenes);
System.out.println("start_frame= "+start_frame);
System.out.println("num_corr_samples="+num_corr_samples);
System.out.println("rstr= "+rstr);
System.out.println("speed_min= "+speed_min);
System.out.println("speed_pref= "+speed_pref);
System.out.println("speed_boost= "+speed_boost);
System.out.println("fat_zero= "+fat_zero);
System.out.println("max_range= "+max_range);
}
float [][] fpixels = new float[num_scenes][];
String [] scene_titles = new String [num_scenes];
for (int nscene = 0; nscene < fpixels.length; nscene++) {
fpixels[nscene] = (float[]) imp_sel.getStack().getPixels(nscene+first_corr+1);
scene_titles[nscene] = imp_sel.getStack().getSliceLabel(nscene+first_corr+1);
}
CuasMotion cuasMotion = new CuasMotion (
clt_parameters, // CLTParameters clt_parameters,
parentCLT, // QuadCLT parentCLT,
debugLevel); // int debugLevel)
String suffix_param = "-offs"+corr_offset+"-pairs"+corr_pairs+"-rstr"+rstr+"-fz"+fat_zero+"-cr"+cent_radius+"-mr"+max_range +
"-ms"+speed_min+"-sp"+speed_pref+"-"+speed_boost;
String imp_name = imp_sel.getTitle();
imp_name = trimSuffix(imp_name,".tif");
imp_name = trimSuffix(imp_name,".tiff");
String vf_extended = imp_name+"-vector_extended"+suffix_param; //
vf_extended_dir= trimSuffix(vf_extended_dir,"/");
String vf_extended_path = vf_extended_dir + "/" + vf_extended+".tiff";
double [][][] extended_scan = getVectorFieldHyper(vf_extended_path); // String path)
if (extended_scan == null) {
System.out.println ("testCuasScanMotion(): Failed to read Vector Field from file, calculatring");
// readVectorField(String path)
boolean show_vector_field = true;
boolean show_2d_correlations = true;
String dbg_title = imp_name;
double [][][] corr2d = show_2d_correlations? new double [num_corr_samples][][] : null;
double [][][] motion_scan = getMotionScan(
false, // batch_mode, // boolean batch_mode,
clt_parameters, // CLTParameters clt_parameters,
cuasMotion, // CuasMotion cuasMotion,
fpixels, // float [][] fpixels,
start_frame, // int start_frame,
corr_pairs, // int corr_pairs,
corr_offset, // int corr_offset,
corr_step, // int corr_step,
smooth, // boolean smooth,
fat_zero, // double fat_zero,
cent_radius, // double cent_radius,
n_recenter, // int n_recenter,
-rstr, // double rstr,
dbg_title, // String title,
corr2d, // double [][][] corr2d, // null or [(fpixels.length - seq_length - start_frame) / corr_step)[][]
debugLevel); // int debugLevel) {
int [] remain = new int [num_corr_samples];
boolean [][] filter5 = filterMotionScan(
motion_scan, // final double [][][] motion_scan,
cuasMotion.tilesX, // final int tilesX)
max_range, // final int range, // 1 or 2
null, // final boolean [][] filtered, // same format as output, previously selected
remain, // final int [] remain){
speed_min, // double speed_min,
speed_pref, // double speed_pref,
speed_boost); // double speed_boost);
// double [][][]
extended_scan = extendMotionScan(
motion_scan, // final double [][][] motion_scan,
filter5, // final boolean [][] filtered, // centers, should be non-overlapped
cuasMotion.tilesX, // final int tilesX)
1, // final int range, // 1 or 2
remain); // final int [] remain)
if ((debugLevel >-4) && (remain !=null)) {
for (int nscan = 0; nscan < remain.length; nscan++) {
System.out.print(nscan+":"+remain[nscan]+", ");
}
System.out.println();
}
String [] slice_titles= new String [num_corr_samples];
for (int nscan = 0; nscan < num_corr_samples; nscan++) {
int frame_cent = start_frame + corr_step * nscan + seq_length/2; // debug only
slice_titles[nscan] = imp_sel.getStack().getSliceLabel(frame_cent+1);
}
if (show_2d_correlations) {
int corr_size = 2 * GPUTileProcessor.DTT_SIZE -1;
double [][] dbg_2d_corrs = ImageDtt.corr_partial_dbg( // not used in lwir
corr2d, // final double [][][] corr_data, // [layer][tile][(2*transform_size-1)*(2*transform_size-1)] // if null - will not calculate
cuasMotion.tilesX, // final int tilesX,
corr_size, //final int corr_size, // 15
clt_parameters.corr_border_contrast, // final double border_contrast,
debugLevel); // final int globalDebugLevel)
ShowDoubleFloatArrays.showArrays(
dbg_2d_corrs,
cuasMotion.tilesX * (corr_size + 1),
cuasMotion.tilesY * (corr_size + 1),
true,
imp_name+"-corr2d"+suffix_param, // "-corr2d"+"-"+frame0+"-"+frame1+"-"+corr_pairs,
slice_titles);
}
if (show_vector_field) {
String [] top_titles = {"vX","vY","strength","fraction","speed", "confidence"};
double [][][] dbg_vf = new double [top_titles.length][num_corr_samples][cuasMotion.tilesX * cuasMotion.tilesY];
for (int nscan = 0; nscan < motion_scan.length; nscan++) {
for (int k = 0; k < top_titles.length; k++) {
Arrays.fill(dbg_vf[k][nscan], Double.NaN);
}
for (int t=0; t<motion_scan[nscan].length; t++) {
if (motion_scan[nscan][t] != null) {
double es = getEffectiveStrength(
motion_scan[nscan][t], // double [] mv_tile, // 4
speed_min, // double speed_min,
speed_pref, // double speed_pref,
speed_boost); //double speed_boost)
if (es > 0 ) {
for (int k = 0; k < 4; k++) {
dbg_vf[k][nscan][t] = motion_scan[nscan][t][k];
}
double vx = motion_scan[nscan][t][INDX_VX];
double vy = motion_scan[nscan][t][INDX_VY];
dbg_vf[4][nscan][t] = Math.sqrt(vx*vx+vy*vy);
dbg_vf[5][nscan][t] = es;
}
}
}
}
String vf_title = imp_name+"-vector_field"+suffix_param; //
ShowDoubleFloatArrays.showArraysHyperstack(
dbg_vf, // double[][][] pixels,
cuasMotion.tilesX, // int width,
vf_title, // String title, "time_derivs-rt"+diff_time_rt+"-rxy"+diff_time_rxy,
slice_titles, // String [] titles, // all slices*frames titles or just slice titles or null
top_titles, // String [] frame_titles, // frame titles or null
true); // boolean show)
// filter
for (int nscan = 0; nscan < motion_scan.length; nscan++) {
for (int t=0; t<motion_scan[nscan].length; t++) {
if (motion_scan[nscan][t] != null) {
if (!filter5[nscan][t]) {
for (int k = 0; k < top_titles.length; k++) {
dbg_vf[k][nscan][t] = Double.NaN;
}
}
}
}
}
String vf_filtered_title = imp_name+"-vector_field_filtered"+suffix_param; //
ShowDoubleFloatArrays.showArraysHyperstack(
dbg_vf, // double[][][] pixels,
cuasMotion.tilesX, // int width,
vf_filtered_title, // String title, "time_derivs-rt"+diff_time_rt+"-rxy"+diff_time_rxy,
slice_titles, // String [] titles, // all slices*frames titles or just slice titles or null
top_titles, // String [] frame_titles, // frame titles or null
true); // boolean show)
double [][][] dbg_ve = new double [top_titles.length][num_corr_samples][cuasMotion.tilesX * cuasMotion.tilesY];
for (int nscan = 0; nscan < motion_scan.length; nscan++) {
for (int k = 0; k < top_titles.length; k++) {
Arrays.fill(dbg_ve[k][nscan], Double.NaN);
}
for (int t=0; t<extended_scan[nscan].length; t++) {
if (extended_scan[nscan][t] != null) {
double es = getEffectiveStrength(
extended_scan[nscan][t], // double [] mv_tile, // 4
speed_min, // double speed_min,
speed_pref, // double speed_pref,
speed_boost); //double speed_boost)
if (es > 0) {
for (int k = 0; k < 4; k++) {
dbg_ve[k][nscan][t] = extended_scan[nscan][t][k];
}
double vx = extended_scan[nscan][t][INDX_VX];
double vy = extended_scan[nscan][t][INDX_VY];
dbg_ve[4][nscan][t] = Math.sqrt(vx*vx+vy*vy);
dbg_ve[5][nscan][t] = es;
}
}
}
}
ShowDoubleFloatArrays.showArraysHyperstack(
dbg_ve, // double[][][] pixels,
cuasMotion.tilesX, // int width,
vf_extended, // String title, "time_derivs-rt"+diff_time_rt+"-rxy"+diff_time_rxy,
slice_titles, // String [] titles, // all slices*frames titles or just slice titles or null
top_titles, // String [] frame_titles, // frame titles or null
true); // boolean show)
}
if (debugLevel > -4) {
System.out.println("scan DONE");
}
}
if (debugLevel > -4) {
System.out.println("Starting render");
}
int frame0 = start_frame + seq_length/2;
float [][] fpixels_rendered = cuasMotion.shiftAndRenderTest(
clt_parameters, // CLTParameters clt_parameters,
fpixels, // final float [][] fpixels,
extended_scan, // final double [][][] vector_field,
frame0, // final int frame0, // for vector_field[0]
corr_step, // final int frame_step,
corr_offset, // final int corr_offset, // interframe distance for correlation
true); // final boolean batch_mode) {
if (debugLevel > -4) {
System.out.println("Render DONE");
}
final int half_accum_range = corr_pairs/2;
final boolean smooth_accum = true;
float [][] fpixels_accumulated = cuasMotion.shiftAndRenderAccumulate(
clt_parameters, // CLTParameters clt_parameters,
fpixels, // final float [][] fpixels,
extended_scan, // final double [][][] vector_field,
frame0, // final int frame0, // for vector_field[0]
corr_step, // final int frame_step,
half_accum_range, // final int half_range,
smooth_accum, // final boolean smooth,
corr_offset, // final int corr_offset, // interframe distance for correlation
true); // final boolean batch_mode) {
// replace center frames with the accumulated ones
for (int nseq = 0; nseq < fpixels_accumulated.length; nseq++){
fpixels_rendered[frame0 + nseq * corr_step] = fpixels_accumulated[nseq];
}
if (debugLevel > -4) {
System.out.println("Accumulation DONE");
}
float [][][] fpixels_hyper = {fpixels,fpixels_rendered};
String [] hyper_titles_top = {"Source","Rendered"};
String hyper_title = imp_name+"-rendered"+suffix_param; //
ImagePlus imp_hyper = ShowDoubleFloatArrays.showArraysHyperstack(
fpixels_hyper, // float[][][] pixels,
cuasMotion.gpu_max_width, // int width,
hyper_title, // String title, "time_derivs-rt"+diff_time_rt+"-rxy"+diff_time_rxy,
scene_titles, // String [] titles, // all slices*frames titles or just slice titles or null
hyper_titles_top, // String [] frame_titles, // frame titles or null
true); // boolean show)
if (debugLevel > -4) {
System.out.println("All DONE");
}
}
}
public static double [][][] getVectorFieldHyper(String path){
int [] wh = new int [2];
String [][] pvf_top_titles = new String[1][];
String [][] pvf_titles = new String[1][];
double [][][] vf_file = ShowDoubleFloatArrays.readDoubleHyperstack(
path, // String path,
wh, // int [] wh, // should be null or int[2]
pvf_top_titles, // String [][] ptop_titles, // should be null or String [1][]
pvf_titles); // String [][] pslice_titles){// should be null or String [1][]
if (vf_file == null) {
return null;
}
int vect_len = vf_file.length;
int num_seq = vf_file[0].length;
int num_tiles = vf_file[0][0].length;
double [][][] vf = new double [num_seq][num_tiles][];
for (int nseq=0; nseq < num_seq; nseq++) {
for (int ntile = 0; ntile < num_tiles; ntile++) if (!Double.isNaN(vf_file[0][nseq][ntile])){
double [] v = new double[vect_len];
for (int i = 0; i < vect_len; i++) {
v[i] = vf_file[i][nseq][ntile];
}
vf[nseq][ntile] = v;
}
}
return vf;
}
public static String trimSuffix(
String name,
String suffix) {
while (name.endsWith(suffix)) {
name = name.substring(0, name.length()-suffix.length());
}
return name;
}
public static boolean [][] filterMotionScan(
final double [][][] motion_scan,
final int tilesX,
final int range, // 1 or 2
final boolean [][] filtered, // same format as output, previously selected
final int [] remain,
double speed_min,
double speed_pref,
double speed_boost ){
final int num_seq = motion_scan.length;
final int num_tiles = motion_scan[0].length;
final int tilesY = num_tiles/ tilesX;
final boolean [][] filter5 = new boolean [num_seq][num_tiles];
final Thread[] threads = ImageDtt.newThreadArray();
final AtomicInteger ai = new AtomicInteger(0);
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() {
public void run() {
TileNeibs tn = new TileNeibs(tilesX, tilesY);
for (int nSeq = ai.getAndIncrement(); nSeq < num_seq; nSeq = ai.getAndIncrement()) {
int num = 0;
double [][] ms = motion_scan[nSeq];
for (int tileY = 0; tileY < tilesY; tileY++) {
for (int tileX = 0; tileX < tilesX; tileX++) {
int ntile = tileX + tilesX * tileY;
if ((ms[ntile] != null) && ((filtered == null) || !filtered[nSeq][ntile])) {
boolean ismax = true;
check_max:{
// double cval = ms[ntile][INDX_STRENGTH]*ms[ntile][INDX_FRAC]; // quality
double cval = getEffectiveStrength(
ms[ntile], // double [] mv_tile, // 4
speed_min, // double speed_min,
speed_pref, // double speed_pref,
speed_boost); //double speed_boost)
if (cval == 0) {
ismax = false;
break check_max;
}
for (int dy = -range; dy <= range; dy++) {
for (int dx = -range; dx <= range; dx++) {
int indx = tn.getNeibIndex(ntile, dx, dy);
if ((indx >= 0) && (ms[indx] != null) && ((filtered == null) || !filtered[nSeq][indx])){ if ((filtered == null) || !filtered[nSeq][indx]) {
// double val = ms[indx][INDX_STRENGTH]*ms[indx][INDX_FRAC]; // quality
// FIXME: calculate once all values before this cycle
double val = getEffectiveStrength(
ms[indx], // double [] mv_tile, // 4
speed_min, // double speed_min,
speed_pref, // double speed_pref,
speed_boost); //double speed_boost)
if (val > cval) {
ismax = false;
break check_max;
}
}
}
}
}
}
filter5[nSeq][ntile] = ismax;
if (ismax) {
num++;
}
}
}
}
if (remain != null) {
remain[nSeq] = num;
}
}
}
};
}
ImageDtt.startAndJoin(threads);
return filter5;
}
private static double getEffectiveStrength(
double [] mv_tile, // 4
double speed_min,
double speed_pref,
double speed_boost) {
if (mv_tile == null ) {
return 0.0;
}
double val = mv_tile[INDX_STRENGTH];
if (mv_tile.length > INDX_STRENGTH) {
val *= mv_tile[INDX_FRAC];
}
if ((speed_min > 0) ||(speed_pref >0)) {
double vx = mv_tile[INDX_VX];
double vy = mv_tile[INDX_VY];
double v = Math.sqrt(vx*vx + vy*vy);
if (v < speed_min) {
return 0;
}
if (v > speed_pref) {
val *= Math.min(v / speed_pref, speed_boost);
}
}
return val;
}
public static double [][][] extendMotionScan(
final double [][][] motion_scan,
final boolean [][] filtered, // centers, should be non-overlapped
final int tilesX,
final int range, // 1 or 2
final int [] remain){
final int num_seq = motion_scan.length;
final int num_tiles = motion_scan[0].length;
final int tilesY = num_tiles/ tilesX;
final double [][][] extended = new double [num_seq][num_tiles][];
final Thread[] threads = ImageDtt.newThreadArray();
final AtomicInteger ai = new AtomicInteger(0);
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() {
public void run() {
TileNeibs tn = new TileNeibs(tilesX, tilesY);
for (int nSeq = ai.getAndIncrement(); nSeq < num_seq; nSeq = ai.getAndIncrement()) {
int num = 0;
double [][] ms = motion_scan[nSeq];
boolean [] centers = filtered[nSeq];
for (int tileY = 0; tileY < tilesY; tileY++) {
for (int tileX = 0; tileX < tilesX; tileX++) {
int ntile = tileX + tilesX * tileY;
if (centers[ntile]) {
num++;
double [] mv = motion_scan[nSeq][ntile];
for (int dy = -range; dy <= range; dy++) {
for (int dx = -range; dx <= range; dx++) {
int indx = tn.getNeibIndex(ntile, dx, dy);
if (indx >=0) {
extended[nSeq][indx] = mv.clone(); // does not need to ne cloned
}
}
}
}
}
}
if (remain != null) {
remain[nSeq] = num;
}
}
}
};
}
ImageDtt.startAndJoin(threads);
return extended;
}
public static double [][][] getMotionScan(
boolean batch_mode,
CLTParameters clt_parameters,
CuasMotion cuasMotion,
float [][] fpixels,
int start_frame,
int corr_pairs,
int corr_offset,
int corr_step,
boolean smooth,
double fat_zero,
double cent_radius,
int n_recenter,
double rstr,
String title,
double [][][] corr2d, // null or [(fpixels.length - seq_length - start_frame) / corr_step)[][]
int debugLevel) {
int seq_length = corr_offset + corr_pairs;
int num_scenes = fpixels.length;
int num_corr_samples = (num_scenes - seq_length - start_frame) / corr_step;
double [][][] motion_scan = new double [num_corr_samples][][];
for (int nscan = 0; nscan < motion_scan.length; nscan++) {
int frame0 = start_frame + corr_step * nscan;
int frame1 = frame0 + corr_offset;
int frame_cent = frame0 + seq_length/2; // debug only
String suffix_param = "-"+frame_cent+"-"+corr_offset+"-"+corr_pairs;
String dbg_suffix = (title != null) ? (title+suffix_param) : null;
TDCorrTile [] tdCorrTiles = cuasMotion.correlatePairs(
clt_parameters, // CLTParameters clt_parameters,
fpixels, // float [][] fpixels,
frame0, // int frame0,
frame1, // int frame1,
corr_pairs, // int frame_len,
smooth, // boolean smooth, // use cosine mask
true, // batch_mode, // final boolean batch_mode,
dbg_suffix, // final String dbg_suffix, // for image_names
debugLevel); // int debugLevel)
// convert to pixel domain and display
double [][] corr_tiles_pd = cuasMotion.convertTDtoPD(
tdCorrTiles, // final TDCorrTile [] tiles,
0xFE, // final int corr_type, // 0xFE
fat_zero, // final double gpu_fat_zero,
debugLevel); // final int debug_level
double [][] vector_field = TDCorrTile.getMismatchVector( // full tiles in gpu (512*512)
corr_tiles_pd, // final double[][] tiles,
rstr, // double rmax,
cent_radius, // final double centroid_radius, // 0 - all same weight, > 0 cosine(PI/2*sqrt(dx^2+dy^2)/rad)
n_recenter, // final int n_recenter); // re-center window around new maximum. 0 -no refines (single-pass)
true); // final boolean calc_fraction ){ // calculate fraction inside center circle
final int corr_size = 2 * GPUTileProcessor.DTT_SIZE -1;
motion_scan[nscan] = vector_field;
// for debugging: to see each 2d correlation
if (corr2d != null) {
corr2d[nscan] = corr_tiles_pd;
}
boolean show_vector_field = (debugLevel>100); // true;
boolean show_2d_correlations = (debugLevel>0); // true;
if (show_2d_correlations) {
double [][] dbg_2d_corrs = ImageDtt.corr_partial_dbg( // not used in lwir
new double [][][] {corr_tiles_pd}, // final double [][][] corr_data, // [layer][tile][(2*transform_size-1)*(2*transform_size-1)] // if null - will not calculate
cuasMotion.tilesX, // final int tilesX,
corr_size, //final int corr_size, // 15
clt_parameters.corr_border_contrast, // final double border_contrast,
debugLevel); // final int globalDebugLevel)
String [] dbg_titles= {"raw"}; // ,"neibs"};
ShowDoubleFloatArrays.showArrays(
dbg_2d_corrs,
cuasMotion.tilesX * (corr_size + 1),
cuasMotion.tilesY * (corr_size + 1),
true,
title+"-corr2d"+suffix_param, // "-corr2d"+"-"+frame0+"-"+frame1+"-"+corr_pairs,
dbg_titles);
}
if (show_vector_field) {
double [][] dbg_vf = new double [3][cuasMotion.tilesX * cuasMotion.tilesY];
String [] dbg_titles = new String[dbg_vf.length];
String [] prefix= {"single"}; // ,"neibs"};
for (int n = 0; n < 1; n++) { // vector_field.length; n++) {
dbg_titles [3*n+0] = prefix[n]+"-vx";
dbg_titles [3*n+1] = prefix[n]+"-vy";
dbg_titles [3*n+2] = prefix[n]+"-str";
}
for (int i = 0; i < dbg_vf.length; i++) {
Arrays.fill(dbg_vf[i], Double.NaN);
}
for (int t=0; t<dbg_vf[0].length; t++) {
if (vector_field[t] != null) {
for (int k = 0; k < 3; k++) {
dbg_vf[k][t] = vector_field[t][k];
}
}
}
ShowDoubleFloatArrays.showArrays(
dbg_vf,
cuasMotion.tilesX,
cuasMotion.tilesY,
true,
title+"-vector_field"+suffix_param,
dbg_titles);
}
}
return motion_scan;
}
/**
* Convert Transform-domain 2D correlation to phase correlation,
* inverse-transform it to pixel domain and return result as
* sparse array of double[] tiles mapped in linescan order. Empty
* tiles are null.
* @param tiles
* @param corr_type
* @param gpu_fat_zero
* @param debug_level
* @return sparse array in line-scan order. Each element either null or double[225]
*/
public double [][] convertTDtoPD(
final TDCorrTile [] tiles,
final int corr_type, // 0xFE
final double gpu_fat_zero,
final int debug_level
) {
return TDCorrTile.convertTDtoPD(
gpuQuad, // final GpuQuad gpuQuad,
tiles, // final TDCorrTile [] tiles,
corr_type, // 0xFEfinal int corr_type, // 0xFE
gpu_fat_zero, // final double gpu_fat_zero,
debug_level); // final int debug_level)
}
public TDCorrTile [] correlatePairs(
CLTParameters clt_parameters,
float [][] fpixels,
int frame0,
int frame1,
int frame_len,
boolean smooth, // use cosine mask
final boolean batch_mode,
final String dbg_suffix, // for image_names
int debugLevel) {
TDCorrTile [] tdCorrTiles = new TDCorrTile[tilesX * tilesY];
double sw = 0;
for (int dframe = 0; dframe < frame_len; dframe++) {
double weight = smooth ? Math.sin((dframe+0.5)/frame_len*Math.PI): 1.0;
String dbg_n_suffix = (dbg_suffix != null)? (dbg_suffix+"-"+dframe) : null;
TDCorrTile [] pairTiles = correlatePair(
clt_parameters, // CLTParameters clt_parameters,
fpixels[frame0+dframe], // float [] fpixels_ref,
fpixels[frame1+dframe], // float [] fpixels_img,
batch_mode, // final boolean batch_mode,
dbg_n_suffix, // final String dbg_suffix, // for image_names
debugLevel); // int debugLevel)
TDCorrTile.accumulate (
tdCorrTiles, // final TDCorrTile [] dst,
pairTiles, // final TDCorrTile [] src,
weight); // final double src_weight)
sw+= weight;
}
double scale = 1.0/sw;
for (TDCorrTile tile:tdCorrTiles) if (tile !=null){
tile.scale(scale);
}
return tdCorrTiles;
}
public void accumulateMotionScene(
CLTParameters clt_parameters,
float [] fpixels,
final double [][] vector_field,
final int erase, // -1 - no, 0 - to 0, 1 - to NaN
final boolean batch_mode,
final double offset_scale,
final double magnitude_scale,
final int tilesX) {
int [] wh = {gpu_max_width, gpu_max_height};
TpTask [] tp_tasks = GpuQuad.setRectilinearMovingTasks(
vector_field, // final double [][] vector_field,
offset_scale, // final double offset_scale,
magnitude_scale, // final double magnitude_scale,
tilesX); // final int tilesX)
image_dtt.setRectilinearReferenceTD(
erase, // final int erase_clt,
fpixels, // final float [] fpixels_ref,
wh, // final int [] wh, // null (use sensor dimensions) or pair {width, height} in pixels
clt_parameters.img_dtt, // final ImageDttParameters imgdtt_params, // Now just extra correlation parameters, later will include, most others
false, // final boolean use_reference_buffer,
tp_tasks, // final TpTask[] tp_tasks,
clt_parameters.gpu_sigma_r, // final double gpu_sigma_r, // 0.9, 1.1
clt_parameters.gpu_sigma_b, // final double gpu_sigma_b, // 0.9, 1.1
clt_parameters.gpu_sigma_g, // final double gpu_sigma_g, // 0.6, 0.7
clt_parameters.gpu_sigma_m, // final double gpu_sigma_m, // = 0.4; // 0.7;
batch_mode? -3: debugLevel); // final int globalDebugLevel)
boolean frame_debug = false;
if (frame_debug) {
renderFromTD (
false, // boolean use_reference,
"render-from-TD"); // String suffix)
}
return;
}
public TDCorrTile [] correlatePair(
CLTParameters clt_parameters,
float [] fpixels_ref,
float [] fpixels_img,
final boolean batch_mode,
final String dbg_suffix, // for image_names
int debugLevel) {
int [] wh = {gpu_max_width, gpu_max_height};
Rectangle woi = new Rectangle (0,0,wh[0], wh[1]);
float [][] fpixels = {fpixels_ref, fpixels_img};
TpTask [][] tp_tasks = GpuQuad.setRectilinearInterTasks(
fpixels, // final float [][] fpixels, // to check for empty (no processing where it images have NaN
wh[0], // final int img_width,
woi, // Rectangle woi,
null); // final double [][][] affine // [2][2][3] affine coefficients to translate common to 2 images
// if (tp_tasks_o != null) {
// for (int i = 0; i < tp_tasks_o.length; i++) tp_tasks_o[i] = tp_tasks[i];
// }
int erase_cltr=-1;
int erase_clt= -1;
image_dtt.setRectilinearReferenceTD(
erase_cltr, // final int erase_clt,
fpixels_ref, // final float [] fpixels_ref,
wh, // final int [] wh, // null (use sensor dimensions) or pair {width, height} in pixels
clt_parameters.img_dtt, // final ImageDttParameters imgdtt_params, // Now just extra correlation parameters, later will include, most others
true, // final boolean use_reference_buffer,
tp_tasks[0], // final TpTask[] tp_tasks,
clt_parameters.gpu_sigma_r, // final double gpu_sigma_r, // 0.9, 1.1
clt_parameters.gpu_sigma_b, // final double gpu_sigma_b, // 0.9, 1.1
clt_parameters.gpu_sigma_g, // final double gpu_sigma_g, // 0.6, 0.7
clt_parameters.gpu_sigma_m, // final double gpu_sigma_m, // = 0.4; // 0.7;
batch_mode? -3: debugLevel); // final int globalDebugLevel)
if (!batch_mode && (dbg_suffix != null)) {
renderFromTD (
true, // boolean use_reference,
"ref"+dbg_suffix); //String suffix
}
float [][][][] fcorr_td = new float [tilesY][tilesX][][];
//null; // no accumulation, use data in GPU
final double gpu_sigma_corr = clt_parameters.getGpuCorrSigma(image_dtt.isMonochrome());
final double gpu_sigma_rb_corr = image_dtt.isMonochrome()? 1.0 : clt_parameters.gpu_sigma_rb_corr;
final double gpu_sigma_log_corr = clt_parameters.getGpuCorrLoGSigma(image_dtt.isMonochrome());
image_dtt.interRectilinearCorrTD(
clt_parameters.img_dtt, //final ImageDttParameters imgdtt_params, // Now just extra correlation parameters, later will include, most others
batch_mode, // final boolean batch_mode,
erase_clt, // final int erase_clt,
fpixels_img, // final float [] fpixels,
wh, // final int [] wh, // null (use sensor dimensions) or pair {width, height} in pixels
tp_tasks[1], // final TpTask[] tp_tasks,
fcorr_td, // final float [][][][] fcorr_td, // [tilesY][tilesX][pair][4*64] transform domain representation of 6 corr pairs
clt_parameters.gpu_sigma_r, // final double gpu_sigma_r, // 0.9, 1.1
clt_parameters.gpu_sigma_b, // final double gpu_sigma_b, // 0.9, 1.1
clt_parameters.gpu_sigma_g, // final double gpu_sigma_g, // 0.6, 0.7
clt_parameters.gpu_sigma_m, // final double gpu_sigma_m, // = 0.4; // 0.7;
gpu_sigma_rb_corr, // final double gpu_sigma_rb_corr, // = 0.5; // apply LPF after accumulating R and B correlation before G, monochrome ? 1.0 :
gpu_sigma_corr, // final double gpu_sigma_corr, // = 0.9;gpu_sigma_corr_m
gpu_sigma_log_corr, // final double gpu_sigma_log_corr, // hpf to reduce dynamic range for correlations
clt_parameters.corr_red, // final double corr_red, // +used
clt_parameters.corr_blue, // final double corr_blue,// +used
-1, // final int sensor_mask_inter, // The bitmask - which sensors to correlate, -1 - all.
debugLevel); // final int globalDebugLevel)
if (!batch_mode && (dbg_suffix != null)) {
renderFromTD (
false, // boolean use_reference,
"img"+dbg_suffix); //String suffix
}
TDCorrTile [] tdCorrTiles = TDCorrTile.getFromGpu(gpuQuad);
return tdCorrTiles;
}
public float [][] shiftAndRenderTest(
CLTParameters clt_parameters,
final float [][] fpixels,
final double [][][] vector_field,
final int frame0, // for vector_field[0]
final int frame_step,
final int corr_offset, // interframe distance for correlation
final boolean batch_mode) {
double magnitude_scale = -1.0; // /(2 * half_step); // here just 1.0 - will use cosine window when accumulating
float [] nan_frame = new float [fpixels[0].length];
float [][] result_frames = new float [fpixels.length][];
Arrays.fill(nan_frame, Float.NaN);
Arrays.fill(result_frames, nan_frame);
int half_step = frame_step/2;
final int erase = 1; //NaN
for (int nseq = 0; nseq < vector_field.length; nseq++) {
int frame_center = frame0 + nseq * frame_step;
for (int dframe = -half_step; dframe < half_step; dframe++) {
int frame = frame_center + dframe;
if ((frame >=0) && (frame < result_frames.length)) {
double offset_scale = (1.0 * dframe) / corr_offset;
accumulateMotionScene(
clt_parameters, // CLTParameters clt_parameters,
fpixels[frame], // float [] fpixels,
vector_field[nseq], // final double [][] vector_field,
erase, // final int erase,
batch_mode, // final boolean batch_mode,
offset_scale, // final double offset_scale,
magnitude_scale, // final double magnitude_scale,
tilesX) ; // final int tilesX) {
result_frames[frame] = floatFromTD(false); // boolean use_reference)
boolean frame_debug = false;
if (frame_debug) {
renderFromTD (
false, // boolean use_reference,
"render-from-TD"); // String suffix)
}
}
}
}
return result_frames;
}
public float [][] shiftAndRenderAccumulate(
CLTParameters clt_parameters,
final float [][] fpixels,
final double [][][] vector_field,
final int frame0, // for vector_field[0]
final int frame_step,
final int half_range,
final boolean smooth,
final int corr_offset, // interframe distance for correlation
final boolean batch_mode) {
// double magnitude_scale = -1.0; // /(2 * half_step); // here just 1.0 - will use cosine window when accumulating
float [] nan_frame = new float [fpixels[0].length];
// float [][] result_frames = new float [fpixels.length][];
float [][] frames_accum = new float [vector_field.length][];
Arrays.fill(nan_frame, Float.NaN);
// Arrays.fill(result_frames, nan_frame);
Arrays.fill(frames_accum, nan_frame);
// int half_step = frame_step/2;
final double [] window_full = new double [2*half_range+1];
double s0 = 1.0;
window_full[half_range] = 1.0;
double k = Math.PI/2/(half_range +0.5);
for (int i = 1; i <= half_range; i ++) {
window_full[half_range+i] = smooth ? (Math.cos(i*k)):1.0;
s0+=2 * window_full[half_range+i];
}
for (int i = 0; i < window_full.length; i ++) {
window_full[i] /= s0;
}
// final int erase = 1; //NaN
for (int nseq = 0; nseq < vector_field.length; nseq++) {
int frame_center = frame0 + nseq * frame_step;
boolean fits = (frame_center >= half_range) && (frame_center < (fpixels.length-half_range));
double [] window = fits? window_full: window_full.clone();
if (!fits) {
s0=0;
for (int i = 0; i < window.length; i++) {
int i1 = frame_center - half_range + i;
if ((i1 >= 0) && (i1 < fpixels.length)) {
s0+= window[i];
}
}
for (int i = 0; i < window.length; i++) {
window[i] /= s0;
}
}
boolean first = true;
for (int dframe = -half_range; dframe < half_range; dframe++) {
double magnitude_scale = window [dframe + half_range];
if (magnitude_scale > 0) { // to make sure <=0 did not get there accidentally
int frame = frame_center + dframe;
if ((frame >= 0) && (frame < fpixels.length) ) {
if ((frame >=0) && (frame < fpixels.length)) {
double offset_scale = (1.0 * dframe) / corr_offset;
int erase = first ? 1 : -1; // NaN on first pass
float [] fpix = first ? fpixels[frame].clone() : fpixels[frame];
if (first) {
for (int i = 0; i < fpix.length; i++) {
fpix[i] *= -1.0;
}
magnitude_scale *= -1.0; // with >0 will set, not accumulate
}
accumulateMotionScene(
clt_parameters, // CLTParameters clt_parameters,
fpix, // float [] fpixels,
vector_field[nseq], // final double [][] vector_field,
erase, // final int erase,
batch_mode, // final boolean batch_mode,
offset_scale, // final double offset_scale,
magnitude_scale, // final double magnitude_scale, // first time - negative (will set)
tilesX) ; // final int tilesX) {
first = false;
// result_frames[frame] = floatFromTD(false); // boolean use_reference)
boolean frame_debug = false;
if (frame_debug) {
renderFromTD (
false, // boolean use_reference,
"render-from-TD_"+frame); // String suffix)
}
}
}
}
}
// get form TD and invert
frames_accum[nseq] = floatFromTD(false); // boolean use_reference)
boolean frame_debug = false;
if (frame_debug) {
renderFromTD (
false, // boolean use_reference,
"render-from-TD_accum"); // String suffix)
}
}
// invert multithreaded
final Thread[] threads = ImageDtt.newThreadArray();
final AtomicInteger ai = new AtomicInteger(0);
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() {
public void run() {
// may be faster if process only where vector_field[nseq][ntile] is not null
for (int nSeq = ai.getAndIncrement(); nSeq < frames_accum.length; nSeq = ai.getAndIncrement()) {
for (int i = 0; i < frames_accum[nSeq].length; i++) {
frames_accum[nSeq][i] *=-1;
}
}
}
};
}
ImageDtt.startAndJoin(threads);
return frames_accum;
}
public void renderFromTD (
boolean use_reference,
String suffix) {
gpuQuad.execImcltRbgAll(
(gpuQuad.getNumColors() <=1), // isMonochrome(),
use_reference,
null); // wh); //int [] wh
// get data back from GPU
final float [][][] iclt_fimg = new float [gpuQuad.getNumSensors()][][];
for (int ncam = 0; ncam < iclt_fimg.length; ncam++) {
iclt_fimg[ncam] = gpuQuad.getRBG(ncam); // updated window
}
int out_width = gpuQuad.getImageWidth();
int out_height = gpuQuad.getImageHeight();
ShowDoubleFloatArrays.showArrays(
iclt_fimg[0],
out_width,
out_height,
true,
suffix);
}
public double [] doubleFromTD (
boolean use_reference) {
gpuQuad.execImcltRbgAll(
(gpuQuad.getNumColors() <=1), // isMonochrome(),
use_reference,
null); // wh); //int [] wh
// get data back from GPU
final float [][][] iclt_fimg = new float [gpuQuad.getNumSensors()][][];
for (int ncam = 0; ncam < iclt_fimg.length; ncam++) {
iclt_fimg[ncam] = gpuQuad.getRBG(ncam); // updated window
}
double [] dpixels = new double [iclt_fimg[0].length];
for (int i = 0; i < dpixels.length; i++) {
dpixels[i] = iclt_fimg[0][0][i];
}
return dpixels;
}
public float [] floatFromTD (
boolean use_reference) {
gpuQuad.execImcltRbgAll(
(gpuQuad.getNumColors() <=1), // isMonochrome(),
use_reference,
null); // wh); //int [] wh
// get data back from GPU
final float [][][] iclt_fimg = new float [gpuQuad.getNumSensors()][][];
for (int ncam = 0; ncam < iclt_fimg.length; ncam++) {
iclt_fimg[ncam] = gpuQuad.getRBG(ncam); // updated window
}
return iclt_fimg[0][0].clone();
}
}
...@@ -245,6 +245,11 @@ public class GpuQuad{ // quad camera description ...@@ -245,6 +245,11 @@ public class GpuQuad{ // quad camera description
return this.quadCLT; return this.quadCLT;
} }
public GPUTileProcessor getGpuTileProcessor() {
return this.gpuTileProcessor;
}
public int getTaskSize() { public int getTaskSize() {
if (rectilinear) return TpTask.getSize(1); if (rectilinear) return TpTask.getSize(1);
return TpTask.getSize(quadCLT.getNumSensors()); return TpTask.getSize(quadCLT.getNumSensors());
...@@ -265,8 +270,8 @@ public class GpuQuad{ // quad camera description ...@@ -265,8 +270,8 @@ public class GpuQuad{ // quad camera description
this.num_cams = quadCLT.getNumSensors(); this.num_cams = quadCLT.getNumSensors();
this.num_all_pairs = Correlation2d.getNumPairs(num_cams); this.num_all_pairs = Correlation2d.getNumPairs(num_cams);
this.num_colors = quadCLT.isMonochrome()?1:3; //num_colors; // maybe should always be 3? this.num_colors = quadCLT.isMonochrome()?1:3; //num_colors; // maybe should always be 3?
this.kernels_hor = quadCLT.getCLTKernels()[0][0][0].length; // kernels_hor; this.kernels_hor = (quadCLT.getCLTKernels() == null) ? 0 : quadCLT.getCLTKernels()[0][0][0].length; // kernels_hor;
this.kernels_vert = quadCLT.getCLTKernels()[0][0].length; // kernels_vert; this.kernels_vert = (quadCLT.getCLTKernels() == null) ? 0 : quadCLT.getCLTKernels()[0][0].length; // kernels_vert;
this.kern_tiles = kernels_hor * kernels_vert * num_colors; this.kern_tiles = kernels_hor * kernels_vert * num_colors;
this.kern_size = kern_tiles * 4 * 64; this.kern_size = kern_tiles * 4 * 64;
if (debug_level >= 100) { if (debug_level >= 100) {
...@@ -4713,12 +4718,79 @@ public class GpuQuad{ // quad camera description ...@@ -4713,12 +4718,79 @@ public class GpuQuad{ // quad camera description
return tp_tasks_out; return tp_tasks_out;
} }
/**
* Prepare tasks for accumulation of multiple images, shifted proportionally to the vector field
* and temporal distance from the middle frame. Accumulation uses the feature developed for the
* thermal camera motion blur correction that allows accumulation in the transform domain. It only
* works with the negative scales, so the result will be a negative image in the TD.
* @param vector_field sparse array of motion vectors (may be longer, only Vx and Vy used). Null elements
* are allowed, they will be skipped, resultin in null TpTask elements.
* @param offset_scale multiply all vectors by this value when calculatingpixel offsets
* @param magnitude_scale Scale data for accumulation (here positive, will be negated
* @param image_width image width in tiles (80 for 640-wide images).
* @return condensed array of TpTask
*/
public static TpTask [] setRectilinearMovingTasks(
final double [][] vector_field,
final double offset_scale,
final double magnitude_scale,
final int tilesX) {
final float fmagnitude_scale = (float) -magnitude_scale;
final int tiles = vector_field.length;
final TpTask [] tp_tasks_full = new TpTask [tiles];
final Thread[] threads = ImageDtt.newThreadArray();
final AtomicInteger ai = new AtomicInteger(00);
final AtomicInteger aTiles = new AtomicInteger(0);
// Not sure which bits are needed to enable GPU processing
final int task_code = (1 << GPUTileProcessor.TASK_TEXT_EN) | (1 << GPUTileProcessor.TASK_CORR_EN) | (1 << GPUTileProcessor.TASK_INTER_EN);
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() {
@Override
public void run() {
for (int nTile = ai.getAndIncrement(); nTile < tiles; nTile = ai.getAndIncrement()) if (vector_field[nTile] != null){
int tileY = nTile / tilesX;
int tileX = nTile % tilesX;;
double dx = vector_field[nTile][0]*offset_scale;
double dy = vector_field[nTile][1]*offset_scale;
double [] cxy0 = { // uniform coordinates for the center scene
(tileX + 0.5) * GPUTileProcessor.DTT_SIZE,
(tileY + 0.5) * GPUTileProcessor.DTT_SIZE};
// check just
TpTask tp_task = new TpTask(1, tileX, tileY); // single "sensor"
tp_task.task = task_code;
tp_task.scale = fmagnitude_scale;
tp_task.setCenterXY(cxy0); // this pair of coordinates will be used by GPU... not yet
tp_task.xy=new float [1][2];
// see where task.disp_dist is used - only in GeometryCorrection, when calculating .xy
tp_task.xy[0][0] = (float) (cxy0[0] + dx);
tp_task.xy[0][1] = (float) (cxy0[1] + dy);
int iTile = aTiles.getAndIncrement();
tp_tasks_full[iTile] = tp_task;
}
}
};
}
ImageDtt.startAndJoin(threads);
final TpTask[] tp_tasks = new TpTask[aTiles.get()];
System.arraycopy(tp_tasks_full, 0, tp_tasks, 0, tp_tasks.length);
return tp_tasks;
}
/**
*
* @param fpixels
* @param img_width
* @param woi
* @param affine
* @return
*/
public static TpTask[][] setRectilinearInterTasks( public static TpTask[][] setRectilinearInterTasks(
final float [][] fpixels, // to check for empty final float [][] fpixels, // to check for empty
final int img_width, final int img_width,
Rectangle woi, Rectangle woi,
final double [][][] affine // [2][2][3] affine coefficients to translate common to 2 images final double [][][] affine_in){ // [2][2][3] affine coefficients to translate common to 2 images
){ final double [][][] affine = (affine_in != null) ? affine_in : new double [][][] {{{1,0,0},{0,1,0}},{{1,0,0},{0,1,0}}};
final int img_height = fpixels[0].length/img_width; final int img_height = fpixels[0].length/img_width;
if (woi == null) { if (woi == null) {
woi = new Rectangle(0,0,img_width,img_height); woi = new Rectangle(0,0,img_width,img_height);
......
...@@ -1995,17 +1995,17 @@ adjusted affines[1] for a pair: 1694564291_293695/1694564778_589341 ...@@ -1995,17 +1995,17 @@ adjusted affines[1] for a pair: 1694564291_293695/1694564778_589341
/** /**
* * Calculate single-tile and neighbors vector fields
* @param clt_parameters * @param clt_parameters meta parameters
* @param fpixels * @param fpixels {reference, other}, each img_width * img_height(calculated)
* @param img_width * @param img_width image width
* @param woi * @param woi image window to process, if null, use full GPU window
* @param affine * @param affine [2][2][3] affine coefficients to translate common to 2 images
* @param tp_tasks_o * @param tp_tasks_o null or TpTask [2][] - will get copy of tasks for both images (different, centers, same tasks)
* @param batch_mode * @param batch_mode avoid graphics debug in batch mode
* @param dbg_suffix * @param dbg_suffix for image_names, such as batch_mode? null: String.format("_%02d", ntry);
* @param debugLevel * @param debugLevel debug level
* @return [2][tilesX*tilesY][3] * @return [2:neibs/single][tilesX*tilesY][3:x,y,str]
*/ */
public static double [][][] rectilinearVectorField( // scene0/scene1 public static double [][][] rectilinearVectorField( // scene0/scene1
final CLTParameters clt_parameters, final CLTParameters clt_parameters,
...@@ -2019,7 +2019,7 @@ adjusted affines[1] for a pair: 1694564291_293695/1694564778_589341 ...@@ -2019,7 +2019,7 @@ adjusted affines[1] for a pair: 1694564291_293695/1694564778_589341
final int debugLevel) { final int debugLevel) {
int [] wh = {img_width, fpixels[0].length/img_width}; int [] wh = {img_width, fpixels[0].length/img_width};
TpTask [][] tp_tasks = GpuQuad.setRectilinearInterTasks( TpTask [][] tp_tasks = GpuQuad.setRectilinearInterTasks(
fpixels, // final float [][] fpixels, // to check for empty fpixels, // final float [][] fpixels, // to check for empty (no processing where it images have NaN
img_width, // final int img_width, img_width, // final int img_width,
woi, // Rectangle woi, woi, // Rectangle woi,
affine); // final double [][][] affine // [2][2][3] affine coefficients to translate common to 2 images affine); // final double [][][] affine // [2][2][3] affine coefficients to translate common to 2 images
...@@ -2139,13 +2139,15 @@ adjusted affines[1] for a pair: 1694564291_293695/1694564778_589341 ...@@ -2139,13 +2139,15 @@ adjusted affines[1] for a pair: 1694564291_293695/1694564778_589341
corr_tiles_pd[0], // final double[][] tiles, corr_tiles_pd[0], // final double[][] tiles,
rln_sngl_rstr, // double rmax, rln_sngl_rstr, // double rmax,
rln_cent_radius, // final double centroid_radius, // 0 - all same weight, > 0 cosine(PI/2*sqrt(dx^2+dy^2)/rad) rln_cent_radius, // final double centroid_radius, // 0 - all same weight, > 0 cosine(PI/2*sqrt(dx^2+dy^2)/rad)
rln_n_recenter); // final int n_recenter); // re-center window around new maximum. 0 -no refines (single-pass) rln_n_recenter, // final int n_recenter); // re-center window around new maximum. 0 -no refines (single-pass)
false); // final boolean calc_fraction ){ // calculate fraction inside center circle
if (corr_tiles_pd.length > 1) { if (corr_tiles_pd.length > 1) {
vector_field[1] = TDCorrTile.getMismatchVector( vector_field[1] = TDCorrTile.getMismatchVector(
corr_tiles_pd[1], // final double[][] tiles, corr_tiles_pd[1], // final double[][] tiles,
rln_neib_rstr, // double rmax, rln_neib_rstr, // double rmax,
rln_cent_radius, // final double centroid_radius, // 0 - all same weight, > 0 cosine(PI/2*sqrt(dx^2+dy^2)/rad) rln_cent_radius, // final double centroid_radius, // 0 - all same weight, > 0 cosine(PI/2*sqrt(dx^2+dy^2)/rad)
rln_n_recenter); // final int n_recenter); // re-center window around new maximum. 0 -no refines (single-pass) rln_n_recenter, // final int n_recenter); // re-center window around new maximum. 0 -no refines (single-pass)
false); // final boolean calc_fraction ){ // calculate fraction inside center circle
} }
boolean show_vector_field = (debugLevel>100); // true; boolean show_vector_field = (debugLevel>100); // true;
boolean show_2d_correlations = (debugLevel>0); // true; boolean show_2d_correlations = (debugLevel>0); // true;
......
...@@ -2412,15 +2412,20 @@ public class Correlation2d { ...@@ -2412,15 +2412,20 @@ public class Correlation2d {
return rslt; return rslt;
} }
public static double [] getMaxXYCm( public static double [] getMaxXYCm(
double [] data, // will be modified if fpn_mask != null; double [] data, // will be modified if fpn_mask != null;
int data_width, // = 2 * transform_size - 1; int data_width, // = 2 * transform_size - 1; // negative - calculate fraction
double radius, // 0 - all same weight, > 0 cosine(PI/2*sqrt(dx^2+dy^2)/rad) double radius, // 0 - all same weight, > 0 cosine(PI/2*sqrt(dx^2+dy^2)/rad)
int refine, // re-center window around new maximum. 0 -no refines (single-pass) int refine, // re-center window around new maximum. 0 -no refines (single-pass)
boolean [] fpn_mask, boolean [] fpn_mask,
boolean ignore_border, // only if fpn_mask != null - ignore tile if maximum touches fpn_mask boolean ignore_border, // only if fpn_mask != null - ignore tile if maximum touches fpn_mask
boolean debug) boolean debug)
{ {
boolean calc_fraction = data_width < 0;
if (calc_fraction) {
data_width = -data_width;
}
int data_height = data.length/data_width; int data_height = data.length/data_width;
int center_xy = (data_width - 1)/2; // = transform_size - 1; int center_xy = (data_width - 1)/2; // = transform_size - 1;
double x0 = center_xy, y0 = center_xy; double x0 = center_xy, y0 = center_xy;
...@@ -2455,6 +2460,7 @@ public class Correlation2d { ...@@ -2455,6 +2460,7 @@ public class Correlation2d {
} }
} }
//calculate as "center of mass" //calculate as "center of mass"
double frac = 0;
if (radius == 0) { if (radius == 0) {
double s0 = 0, sx=0,sy = 0; double s0 = 0, sx=0,sy = 0;
for (int iy = 0; iy < data_height; iy++) { for (int iy = 0; iy < data_height; iy++) {
...@@ -2474,9 +2480,11 @@ public class Correlation2d { ...@@ -2474,9 +2480,11 @@ public class Correlation2d {
} }
x0 += sx / s0; x0 += sx / s0;
y0 += sy / s0; y0 += sy / s0;
// s00 = s0;
} else { } else {
double radius2 = radius*radius; double radius2 = radius*radius;
for (int nref = 0; nref <= refine; nref++) { for (int nref = 0; nref <= refine; nref++) {
double s_other = 0;
double s0 = 0, sx=0,sy = 0; double s0 = 0, sx=0,sy = 0;
for (int iy = 0; iy < data_height; iy++) { for (int iy = 0; iy < data_height; iy++) {
double y = iy - y0; double y = iy - y0;
...@@ -2492,19 +2500,33 @@ public class Correlation2d { ...@@ -2492,19 +2500,33 @@ public class Correlation2d {
sx += d * x; sx += d * x;
sy += d * y; sy += d * y;
} }
} else if (calc_fraction) {
double d = data[iy * data_width + ix];
if (d > 0) { // ignore negative
s_other += d;
}
} }
} }
} }
x0 += sx / s0; x0 += sx / s0;
y0 += sy / s0; y0 += sy / s0;
frac = s0 / (s0 + s_other);
} }
} }
if (calc_fraction) {
double [] rslt = {x0 - center_xy, y0 - center_xy, mx, frac};
if (debug){
System.out.println("getMaxXYCm() -> "+rslt[0]+":"+rslt[1] +" ("+rslt[2]+"), frac = " + rslt[3]);
}
return rslt;
} else {
double [] rslt = {x0 - center_xy, y0 - center_xy, mx}; double [] rslt = {x0 - center_xy, y0 - center_xy, mx};
if (debug){ if (debug){
System.out.println("getMaxXYCm() -> "+rslt[0]+":"+rslt[1]); System.out.println("getMaxXYCm() -> "+rslt[0]+":"+rslt[1] +" ("+rslt[2]+")");
} }
return rslt; return rslt;
} }
}
/** /**
......
...@@ -699,6 +699,19 @@ min_str_neib_fpn 0.35 ...@@ -699,6 +699,19 @@ min_str_neib_fpn 0.35
public double cuas_multi_strength = 0.45; // maximal strength to use multi-tile DSI public double cuas_multi_strength = 0.45; // maximal strength to use multi-tile DSI
public double cuas_reliable_str = 0.8; // use for relaible tiles if INTER-INTRA-LMA is available, not just DSI_MAIN public double cuas_reliable_str = 0.8; // use for relaible tiles if INTER-INTRA-LMA is available, not just DSI_MAIN
public double cuas_fat_zero = 1000.0; // phase correlation fat zero
public double cuas_cent_radius = 4.0; // centroids center radius
public int cuas_n_recenter = 2; // when cosine window, re-center window these many times
public double cuas_rstr = 0.001; // minimal phase correlation maximums relative to max str
public boolean cuas_smooth = true; // used cosine window when averaging correlations
public int cuas_corr_pairs = 50; // number of correlation pairs to accumulate
public int cuas_corr_offset = 20; // offset between motion detection pairs
public boolean cuas_half_step = false; // half step (=cuas_corr_offset/2) when scanning for motion
public int cuas_max_range = 2; // how far to extend local max: 1 3x3 neighbors, 2 - 5x5 neighbs
// boosting weight of moving targets
public double cuas_speed_min = 0.2; // minimal pixels per range (per cuas_corr_offset)
public double cuas_speed_pref = 0.5; // preferable speed (boost weights for faster targets)
public double cuas_speed_boost = 2.5; // speed boost limit
public boolean cuas_debug = false; // save debug images (and show them if not in batch mode) public boolean cuas_debug = false; // save debug images (and show them if not in batch mode)
public boolean cuas_step_debug = false; // save debug images during per-step cuas recalculation (and show them if not in batch mode) public boolean cuas_step_debug = false; // save debug images during per-step cuas recalculation (and show them if not in batch mode)
...@@ -2106,6 +2119,32 @@ min_str_neib_fpn 0.35 ...@@ -2106,6 +2119,32 @@ min_str_neib_fpn 0.35
gd.addNumericField("Reliable strength", this.cuas_reliable_str, 5,7,"", gd.addNumericField("Reliable strength", this.cuas_reliable_str, 5,7,"",
"Reliable tile strength when combo-dsi is available."); "Reliable tile strength when combo-dsi is available.");
gd.addMessage("=== CUAS motion detection ===");
gd.addNumericField("Phase correlation fat zero", this.cuas_fat_zero, 5,8,"",
"Phase correlation fat zero for motion detection.");
gd.addNumericField("Centroids radius", this.cuas_cent_radius, 5,8,"",
"Centroids radius for maximums isolation.");
gd.addNumericField("Recenter centroid", this.cuas_n_recenter, 0,3,"",
"when cosine window, re-center window these many times");
gd.addNumericField("Minimal correlation strength", this.cuas_rstr, 5,8,"",
"Minimal phase correlation maximums (negative value - relative to the maximal strength of the whole images correlations).");
gd.addCheckbox ("Smooth weights", this.cuas_smooth,
"Apply cosine weights when averaging a sequence of correlation pairs.");
gd.addNumericField("Number of pairs", this.cuas_corr_pairs, 0,3,"",
"The number of correlation pairs to accumulate.");
gd.addNumericField("Pairs offset", this.cuas_corr_offset, 0,3,"",
"Offset between the correlation pairs");
gd.addCheckbox ("Half scan step", this.cuas_half_step,
"Reduce step for motion detection = offset/2, if false = offset.");
gd.addNumericField("Local max range", this.cuas_max_range, 0,3,"",
"While filtering local correlation maximums: 1 - 3x3 neighbors, 2 - 5x5 ones.");
gd.addNumericField("Minimal speed", this.cuas_speed_min, 5,8,"ppr",
"Minimal target speed in pixels per range (per cuas_corr_offset).");
gd.addNumericField("Preferable speed", this.cuas_speed_pref, 5,8,"ppr",
"Boost effective strength when speed is above this.");
gd.addNumericField("Maximal speed boost", this.cuas_speed_boost, 5,8,"",
"Maximal speed-caused effective strength boost.");
gd.addMessage("=== Debug ==="); gd.addMessage("=== Debug ===");
gd.addCheckbox ("Save/show debug images", this.cuas_debug, gd.addCheckbox ("Save/show debug images", this.cuas_debug,
...@@ -3058,6 +3097,19 @@ min_str_neib_fpn 0.35 ...@@ -3058,6 +3097,19 @@ min_str_neib_fpn 0.35
this.cuas_multi_strength = gd.getNextNumber(); this.cuas_multi_strength = gd.getNextNumber();
this.cuas_reliable_str = gd.getNextNumber(); this.cuas_reliable_str = gd.getNextNumber();
this.cuas_fat_zero = gd.getNextNumber();
this.cuas_cent_radius = gd.getNextNumber();
this.cuas_n_recenter = (int) gd.getNextNumber();
this.cuas_rstr = gd.getNextNumber();
this.cuas_smooth = gd.getNextBoolean();
this.cuas_corr_pairs = (int) gd.getNextNumber();
this.cuas_corr_offset = (int) gd.getNextNumber();
this.cuas_half_step = gd.getNextBoolean();
this.cuas_max_range = (int) gd.getNextNumber();
this.cuas_speed_min = gd.getNextNumber();
this.cuas_speed_pref = gd.getNextNumber();
this.cuas_speed_boost = gd.getNextNumber();
this.cuas_debug = gd.getNextBoolean(); this.cuas_debug = gd.getNextBoolean();
this.cuas_step_debug = gd.getNextBoolean(); this.cuas_step_debug = gd.getNextBoolean();
...@@ -3933,6 +3985,20 @@ min_str_neib_fpn 0.35 ...@@ -3933,6 +3985,20 @@ min_str_neib_fpn 0.35
properties.setProperty(prefix+"cuas_multi_strength", this.cuas_multi_strength+""); // double properties.setProperty(prefix+"cuas_multi_strength", this.cuas_multi_strength+""); // double
properties.setProperty(prefix+"cuas_reliable_str", this.cuas_reliable_str+""); // double properties.setProperty(prefix+"cuas_reliable_str", this.cuas_reliable_str+""); // double
properties.setProperty(prefix+"cuas_fat_zero", this.cuas_fat_zero+""); // double
properties.setProperty(prefix+"cuas_cent_radius", this.cuas_cent_radius+""); // double
properties.setProperty(prefix+"cuas_n_recenter", this.cuas_n_recenter+""); // int
properties.setProperty(prefix+"cuas_rstr", this.cuas_rstr+""); // double
properties.setProperty(prefix+"cuas_smooth", this.cuas_smooth+""); // boolean
properties.setProperty(prefix+"cuas_corr_pairs", this.cuas_corr_pairs+""); // int
properties.setProperty(prefix+"cuas_corr_offset", this.cuas_corr_offset+""); // int
properties.setProperty(prefix+"cuas_half_step", this.cuas_half_step+""); // boolean
properties.setProperty(prefix+"cuas_max_range", this.cuas_max_range+""); // int
properties.setProperty(prefix+"cuas_speed_min", this.cuas_speed_min+""); // double
properties.setProperty(prefix+"cuas_speed_pref", this.cuas_speed_pref+""); // double
properties.setProperty(prefix+"cuas_speed_boost", this.cuas_speed_boost+""); // double
properties.setProperty(prefix+"cuas_debug", this.cuas_debug+""); // boolean properties.setProperty(prefix+"cuas_debug", this.cuas_debug+""); // boolean
properties.setProperty(prefix+"cuas_step_debug", this.cuas_step_debug+""); // boolean properties.setProperty(prefix+"cuas_step_debug", this.cuas_step_debug+""); // boolean
...@@ -4780,6 +4846,22 @@ min_str_neib_fpn 0.35 ...@@ -4780,6 +4846,22 @@ min_str_neib_fpn 0.35
if (properties.getProperty(prefix+"cuas_multi_strength")!=null) this.cuas_multi_strength=Double.parseDouble(properties.getProperty(prefix+"cuas_multi_strength")); if (properties.getProperty(prefix+"cuas_multi_strength")!=null) this.cuas_multi_strength=Double.parseDouble(properties.getProperty(prefix+"cuas_multi_strength"));
if (properties.getProperty(prefix+"cuas_reliable_str")!=null) this.cuas_reliable_str=Double.parseDouble(properties.getProperty(prefix+"cuas_reliable_str")); if (properties.getProperty(prefix+"cuas_reliable_str")!=null) this.cuas_reliable_str=Double.parseDouble(properties.getProperty(prefix+"cuas_reliable_str"));
if (properties.getProperty(prefix+"cuas_fat_zero")!=null) this.cuas_fat_zero=Double.parseDouble(properties.getProperty(prefix+"cuas_fat_zero"));
if (properties.getProperty(prefix+"cuas_cent_radius")!=null) this.cuas_cent_radius=Double.parseDouble(properties.getProperty(prefix+"cuas_cent_radius"));
if (properties.getProperty(prefix+"cuas_n_recenter")!=null) this.cuas_n_recenter=Integer.parseInt(properties.getProperty(prefix+"cuas_n_recenter"));
if (properties.getProperty(prefix+"cuas_rstr")!=null) this.cuas_rstr=Double.parseDouble(properties.getProperty(prefix+"cuas_rstr"));
if (properties.getProperty(prefix+"cuas_smooth")!=null) this.cuas_smooth=Boolean.parseBoolean(properties.getProperty(prefix+"cuas_smooth"));
if (properties.getProperty(prefix+"cuas_corr_pairs")!=null) this.cuas_corr_pairs=Integer.parseInt(properties.getProperty(prefix+"cuas_corr_pairs"));
if (properties.getProperty(prefix+"cuas_corr_offset")!=null) this.cuas_corr_offset=Integer.parseInt(properties.getProperty(prefix+"cuas_corr_offset"));
if (properties.getProperty(prefix+"cuas_half_step")!=null) this.cuas_half_step=Boolean.parseBoolean(properties.getProperty(prefix+"cuas_half_step"));
if (properties.getProperty(prefix+"cuas_max_range")!=null) this.cuas_max_range=Integer.parseInt(properties.getProperty(prefix+"cuas_max_range"));
if (properties.getProperty(prefix+"cuas_speed_min")!=null) this.cuas_speed_min=Double.parseDouble(properties.getProperty(prefix+"cuas_speed_min"));
if (properties.getProperty(prefix+"cuas_speed_pref")!=null) this.cuas_speed_pref=Double.parseDouble(properties.getProperty(prefix+"cuas_speed_pref"));
if (properties.getProperty(prefix+"cuas_speed_boost")!=null) this.cuas_speed_boost=Double.parseDouble(properties.getProperty(prefix+"cuas_speed_boost"));
if (properties.getProperty(prefix+"cuas_debug")!=null) this.cuas_debug=Boolean.parseBoolean(properties.getProperty(prefix+"cuas_debug")); if (properties.getProperty(prefix+"cuas_debug")!=null) this.cuas_debug=Boolean.parseBoolean(properties.getProperty(prefix+"cuas_debug"));
if (properties.getProperty(prefix+"cuas_step_debug")!=null) this.cuas_step_debug=Boolean.parseBoolean(properties.getProperty(prefix+"cuas_step_debug")); if (properties.getProperty(prefix+"cuas_step_debug")!=null) this.cuas_step_debug=Boolean.parseBoolean(properties.getProperty(prefix+"cuas_step_debug"));
...@@ -5628,6 +5710,19 @@ min_str_neib_fpn 0.35 ...@@ -5628,6 +5710,19 @@ min_str_neib_fpn 0.35
imp.cuas_multi_strength = this.cuas_multi_strength; imp.cuas_multi_strength = this.cuas_multi_strength;
imp.cuas_reliable_str = this.cuas_reliable_str; imp.cuas_reliable_str = this.cuas_reliable_str;
imp.cuas_fat_zero = this.cuas_fat_zero;
imp.cuas_cent_radius = this.cuas_cent_radius;
imp.cuas_n_recenter = this.cuas_n_recenter;
imp.cuas_rstr = this.cuas_rstr;
imp.cuas_smooth = this.cuas_smooth;
imp.cuas_corr_pairs = this.cuas_corr_pairs;
imp.cuas_corr_offset = this.cuas_corr_offset;
imp.cuas_half_step = this.cuas_half_step;
imp.cuas_max_range = this.cuas_max_range;
imp.cuas_speed_min = this.cuas_speed_min;
imp.cuas_speed_pref = this.cuas_speed_pref;
imp.cuas_speed_boost = this.cuas_speed_boost;
imp.cuas_debug = this.cuas_debug; imp.cuas_debug = this.cuas_debug;
imp.cuas_step_debug = this.cuas_step_debug; imp.cuas_step_debug = this.cuas_step_debug;
......
...@@ -244,6 +244,14 @@ public class QuadCLTCPU { ...@@ -244,6 +244,14 @@ public class QuadCLTCPU {
public ImagePlus imp_center_average = null; public ImagePlus imp_center_average = null;
public CorrectionFPN correctionFPN = null; public CorrectionFPN correctionFPN = null;
public int getWidth() {
return tp.getTilesX()*tp.getTileSize();
}
public int getHeight() {
return tp.getTilesY()*tp.getTileSize();
}
public boolean dsiIsFromCombo() { public boolean dsiIsFromCombo() {
return dsi_from_combo; return dsi_from_combo;
} }
...@@ -5943,99 +5951,12 @@ public class QuadCLTCPU { ...@@ -5943,99 +5951,12 @@ public class QuadCLTCPU {
readX3dDirectory(correctionsParameters.getModelName(image_name)); readX3dDirectory(correctionsParameters.getModelName(image_name));
String file_name = image_name + suffix; String file_name = image_name + suffix;
String file_path = x3d_path + Prefs.getFileSeparator() + file_name + ".tiff"; String file_path = x3d_path + Prefs.getFileSeparator() + file_name + ".tiff";
return readFloatArray( return ShowDoubleFloatArrays.readFloatArray(
file_path, // String file_path, file_path, // String file_path,
num_slices, // int num_slices, // (0 - all) num_slices, // int num_slices, // (0 - all)
wh); // int [] wh) wh); // int [] wh)
} }
public static float [][] readFloatArray(
String file_path,
int num_slices, // (0 - all)
int [] wh) {
ImagePlus imp = null;
try {
imp = new ImagePlus(file_path);
} catch (Exception e) {
System.out.println ("Failed to open "+file_path+", maybe will generate it");
}
if ((imp == null) || (imp.getTitle() == null) || (imp.getTitle().equals(""))) {
return null;
}
System.out.println ("Read "+(imp.getStack().getSize())+" slices from "+file_path);
return readFloatArray(
imp, // ImagePlus imp,
num_slices, //int num_slices, // (0 - all)
wh); // int [] wh)
/*
ImageStack imageStack = imp.getStack();
int nChn=imageStack.getSize();
if ((num_slices > 0) && (num_slices < nChn)) {
nChn = num_slices;
}
float [][] result = new float [nChn][];
for (int n = 0; n < nChn; n++) {
result[n] = (float[]) imageStack.getPixels(n + 1);
}
if (wh != null) {
wh[0] = imp.getWidth();
wh[1] = imp.getHeight();
}
return result;
*/
}
public static double [][] readDoubleArray(
ImagePlus imp,
int num_slices, // (0 - all)
int [] wh) {
float [][] fdata = readFloatArray(
imp, // ImagePlus imp,
num_slices, // int num_slices, // (0 - all)
wh); // int [] wh)
if (fdata == null) {
return null;
}
double [][] result = new double [fdata.length][];
for (int n = 0; n < fdata.length; n++) if (fdata[n]!=null) {
result[n] = new double [fdata[n].length];
for (int i = 0; i < fdata[n].length; i++) {
result[n][i] = fdata[n][i];
}
}
return result;
}
public static float [][] readFloatArray(
ImagePlus imp,
int num_slices, // (0 - all)
int [] wh) {
if ((imp == null) || (imp.getTitle() == null) || (imp.getTitle().equals(""))) {
return null;
}
ImageStack imageStack = imp.getStack();
int nChn=imageStack.getSize();
if ((num_slices > 0) && (num_slices < nChn)) {
nChn = num_slices;
}
float [][] result = new float [nChn][];
for (int n = 0; n < nChn; n++) {
result[n] = (float[]) imageStack.getPixels(n + 1);
}
if (wh != null) {
wh[0] = imp.getWidth();
wh[1] = imp.getHeight();
}
return result;
}
public ImagePlus readImagePlusFromModelDirectory( public ImagePlus readImagePlusFromModelDirectory(
......
...@@ -208,7 +208,7 @@ public class TDCorrTile { ...@@ -208,7 +208,7 @@ public class TDCorrTile {
* Get number of defined (non-null) tiles * Get number of defined (non-null) tiles
* @param tiles sparse array of tiles * @param tiles sparse array of tiles
* @param indices - null or int [tiles.length] that will be set to have unique * @param indices - null or int [tiles.length] that will be set to have unique
* indices for non-null tiles. Indices for non-null tiles will not be modified * indices for non-null tiles. Indices for null tiles will be set to -1
* @return number of non-null tiles * @return number of non-null tiles
*/ */
public static int getNumTiles( public static int getNumTiles(
...@@ -223,6 +223,8 @@ public class TDCorrTile { ...@@ -223,6 +223,8 @@ public class TDCorrTile {
public void run() { public void run() {
for (int iTile = ai.getAndIncrement(); iTile < tiles.length; iTile = ai.getAndIncrement()) if (tiles[iTile] != null) { for (int iTile = ai.getAndIncrement(); iTile < tiles.length; iTile = ai.getAndIncrement()) if (tiles[iTile] != null) {
indices[iTile] = anum_tiles.getAndIncrement(); indices[iTile] = anum_tiles.getAndIncrement();
} else {
indices[iTile] = -1;
} }
} }
}; };
...@@ -458,16 +460,27 @@ public class TDCorrTile { ...@@ -458,16 +460,27 @@ public class TDCorrTile {
return mapped_corrs; return mapped_corrs;
} }
/**
* Calculate per-tile motion vectors {x,y,strength_over_min) using centroid approach
* @param tiles sparse array of correlation tiles (now 15*15=255 pix)
* @param rmax minimal tile strength relative to the absolute maximum strength. if negative - use absolute, not relative
* @param centroid_radius 0 - all same weight, > 0 cosine(PI/2*sqrt(dx^2+dy^2)/rad)
* @param n_recenter re-center window around new maximum. 0 -no refines (single-pass)
* @return per-tile array of triplets (x,y,strength-min_strength)
*/
public static double [][] getMismatchVector( public static double [][] getMismatchVector(
final double [][] tiles, final double [][] tiles,
double rmax, double rmax, // minimal tile strength relative to the absolute maximum strength.
final double centroid_radius, // 0 - all same weight, > 0 cosine(PI/2*sqrt(dx^2+dy^2)/rad) final double centroid_radius, // 0 - all same weight, > 0 cosine(PI/2*sqrt(dx^2+dy^2)/rad)
final int n_recenter){ // re-center window around new maximum. 0 -no refines (single-pass) final int n_recenter, // re-center window around new maximum. 0 -no refines (single-pass)
final boolean calc_fraction ){ // calculate fraction inside center circle
double [][] vector_field = new double [tiles.length][]; double [][] vector_field = new double [tiles.length][];
final int corr_size = 2 * GPUTileProcessor.DTT_SIZE - 1; final int corr_size = (calc_fraction? -1 : 1) * (2 * GPUTileProcessor.DTT_SIZE - 1); // negate to force fraction calculation
final Thread[] threads = ImageDtt.newThreadArray(); final Thread[] threads = ImageDtt.newThreadArray();
final AtomicInteger ai = new AtomicInteger(0); final AtomicInteger ai = new AtomicInteger(0);
final AtomicInteger ati = new AtomicInteger(0); final AtomicInteger ati = new AtomicInteger(0);
double amax = 0;
if (rmax > 0) {
final double [] th_max = new double [threads.length]; final double [] th_max = new double [threads.length];
for (int ithread = 0; ithread < threads.length; ithread++) { for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() { threads[ithread] = new Thread() {
...@@ -484,28 +497,33 @@ public class TDCorrTile { ...@@ -484,28 +497,33 @@ public class TDCorrTile {
}; };
} }
ImageDtt.startAndJoin(threads); ImageDtt.startAndJoin(threads);
double amax = th_max[0]; amax = th_max[0];
for (int i = 1; i < th_max.length; i++) { for (int i = 1; i < th_max.length; i++) {
if (th_max[i] > amax) { if (th_max[i] > amax) {
amax = th_max[i]; amax = th_max[i];
} }
} }
final double min_str = rmax * amax; }
final double min_str = (rmax > 0 ) ?rmax * amax : (-rmax);
ai.set(0); ai.set(0);
final int dbg_tile = -(33+35*80);
for (int ithread = 0; ithread < threads.length; ithread++) { for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() { threads[ithread] = new Thread() {
public void run() { public void run() {
for (int iTile = ai.getAndIncrement(); iTile < tiles.length; iTile = ai.getAndIncrement()) if (tiles[iTile] != null) { for (int iTile = ai.getAndIncrement(); iTile < tiles.length; iTile = ai.getAndIncrement()) if (tiles[iTile] != null) {
if (iTile == dbg_tile) {
System.out.println("getMismatchVector(): iTile="+iTile);
}
double [] mv = Correlation2d.getMaxXYCm( // last, average double [] mv = Correlation2d.getMaxXYCm( // last, average
tiles[iTile], // corrs.length-1], // double [] data, tiles[iTile], // corrs.length-1], // double [] data,
corr_size, // int data_width, // = 2 * transform_size - 1; corr_size, // int data_width, // = 2 * transform_size - 1; // negative - will return center fraction
centroid_radius, // double radius, // 0 - all same weight, > 0 cosine(PI/2*sqrt(dx^2+dy^2)/rad) centroid_radius, // double radius, // 0 - all same weight, > 0 cosine(PI/2*sqrt(dx^2+dy^2)/rad)
n_recenter, // int refine, // re-center window around new maximum. 0 -no refines (single-pass) n_recenter, // int refine, // re-center window around new maximum. 0 -no refines (single-pass)
null, // boolean [] fpn_mask, null, // boolean [] fpn_mask,
false, // boolean ignore_border, // only if fpn_mask != null - ignore tile if maximum touches fpn_mask false, // boolean ignore_border, // only if fpn_mask != null - ignore tile if maximum touches fpn_mask
false); // boolean debug) false); // boolean debug)
if (mv[2] > min_str) { if (mv[2] > min_str) {
vector_field[iTile] = new double [] {mv[0], mv[1], mv[2]-min_str}; vector_field[iTile] = new double [] {mv[0], mv[1], mv[2]-min_str, mv[3]};
} }
} }
} }
......
...@@ -111,7 +111,7 @@ public class VegetationSynthesis { ...@@ -111,7 +111,7 @@ public class VegetationSynthesis {
int [] wh = new int [2]; int [] wh = new int [2];
int model_slices = imp_model.getStackSize(); int model_slices = imp_model.getStackSize();
System.out.println("testSynthetic(): read model from: " + path_model+", got "+model_slices+" slices"); System.out.println("testSynthetic(): read model from: " + path_model+", got "+model_slices+" slices");
double [][] model_data = QuadCLT.readDoubleArray( double [][] model_data = ShowDoubleFloatArrays.readDoubleArray(
imp_model, // ImagePlus imp, imp_model, // ImagePlus imp,
0, // int num_slices, // (0 - all) 0, // int num_slices, // (0 - all)
wh); // int [] wh); // int [] wh) wh); // int [] wh); // int [] wh)
...@@ -156,7 +156,7 @@ public class VegetationSynthesis { ...@@ -156,7 +156,7 @@ public class VegetationSynthesis {
} else { } else {
int scene_offs_slices = imp_scene_offs.getStackSize(); int scene_offs_slices = imp_scene_offs.getStackSize();
System.out.println("testSynthetic(): read scene offsets from: " + path_scene_offs +", got "+scene_offs_slices+" slices, will add them to the rendered images"); System.out.println("testSynthetic(): read scene offsets from: " + path_scene_offs +", got "+scene_offs_slices+" slices, will add them to the rendered images");
scene_offs = QuadCLT.readDoubleArray( scene_offs = ShowDoubleFloatArrays.readDoubleArray(
imp_scene_offs, // ImagePlus imp, imp_scene_offs, // ImagePlus imp,
0, // int num_slices, // (0 - all) 0, // int num_slices, // (0 - all)
wh); // int [] wh); // int [] wh) wh); // int [] wh); // int [] wh)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment