Commit d8f3ec21 authored by Andrey Filippov's avatar Andrey Filippov

Optimizing pairs generation

parent 1cc8ef8f
......@@ -2,6 +2,7 @@ package com.elphel.imagej.orthomosaic;
import java.awt.Point;
import java.awt.Rectangle;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
......@@ -48,6 +49,13 @@ public class ComboMatch {
public static Properties [] imp_prop; // = new Properties[2];
public static int gpu_max_width= 4096;
public static int gpu_max_height= 4096;
public static String [] FILES_LISTS_PATHS = {
"/media/elphel/SSD3-4GB/lwir16-proc/ortho_videos/maps_sep12-13_50-25-50-75-100m",
"/media/elphel/SSD3-4GB/lwir16-proc/ortho_videos/maps_19_sep13_25-50-75-100m",
"/media/elphel/SSD3-4GB/lwir16-proc/ortho_videos/maps_nov3_50-75",
"/media/elphel/SSD3-4GB/lwir16-proc/ortho_videos/maps_sep12-50m",
"/media/elphel/SSD3-4GB/lwir16-proc/ortho_videos/maps_sep12-13_50-25-50-75-100m-SUBSET"};
public static boolean openTestPairGps(
CLTParameters clt_parameters,
......@@ -110,13 +118,9 @@ public class ComboMatch {
// String orthoMapsCollection_path = "/media/elphel/SSD3-4GB/lwir16-proc/ortho_videos/maps_10_short.data";
// String files_list_path = "/media/elphel/SSD3-4GB/lwir16-proc/ortho_videos/maps_19_sep13_25-50-75-100m.list";
// String orthoMapsCollection_path = "/media/elphel/SSD3-4GB/lwir16-proc/ortho_videos/maps_19_sep13_25-50-75-100m.data";
String [] files_lists_paths = {
"/media/elphel/SSD3-4GB/lwir16-proc/ortho_videos/maps_sep12-13_50-25-50-75-100m",
"/media/elphel/SSD3-4GB/lwir16-proc/ortho_videos/maps_19_sep13_25-50-75-100m",
"/media/elphel/SSD3-4GB/lwir16-proc/ortho_videos/maps_nov3_50-75",
"/media/elphel/SSD3-4GB/lwir16-proc/ortho_videos/maps_sep12-50m",
"/media/elphel/SSD3-4GB/lwir16-proc/ortho_videos/maps_sep12-13_50-25-50-75-100m-SUBSET"};
int default_list_choice = 0; // files_lists_paths.length-1;
// String [] files_lists_paths = FILES_LISTS_PATHS;
// int default_list_choice = 2; // files_lists_paths.length-1;
// String files_list_path = "/media/elphel/SSD3-4GB/lwir16-proc/ortho_videos/maps_nov3_50-75.list";
// String orthoMapsCollection_path = "/media/elphel/SSD3-4GB/lwir16-proc/ortho_videos/maps_nov3_50-75.data";
//maps_nov3_50-75
......@@ -155,7 +159,8 @@ public class ComboMatch {
int gpu_height= clt_parameters.imp.rln_gpu_height; // 3008;
int zoom_lev = -3; // 0; // +1 - zoom in twice, -1 - zoom out twice
boolean show_combo = false; // true;
boolean create_overlaps = false;
boolean create_pairwise_matches = false;
boolean create_pairwise_affines = false;
boolean equalize_overlaps = false;
boolean create_map = false;
boolean create_equalize = false;
......@@ -180,7 +185,7 @@ public class ComboMatch {
String [] suffixes_bl_bc= {"","-BL","-BC"};
boolean log_append = clt_parameters.imp.pwise_log_append;
String log_path = clt_parameters.imp.pwise_log_path;
String omtch_img_set = clt_parameters.imp.omtch_img_set;
int suffix_bc_bl_indx = 2;
if (!use_marked_image) {
......@@ -191,9 +196,9 @@ public class ComboMatch {
pattern_match = false;
}
GenericJTabbedDialog gd = new GenericJTabbedDialog("Set image pair",1200,800);
gd.addChoice ("Files list/data path (w/o extension):", files_lists_paths, files_lists_paths[default_list_choice]);
// gd.addChoice ("Files list/data path (w/o extension):", FILES_LISTS_PATHS, FILES_LISTS_PATHS[default_list_choice]);
gd.addChoice ("Files list/data path (w/o extension):", FILES_LISTS_PATHS, omtch_img_set);
gd.addCheckbox ("Use saved maps collection", use_saved_collection, "If false - use files list.");
gd.addCheckbox ("Save maps collection", save_collection, "Save maps collection to be able to restore.");
gd.addCheckbox ("Process correlations", process_correlation, "false to skip to just regenerate new save file.");
......@@ -230,7 +235,8 @@ public class ComboMatch {
// gd.addCheckbox ("Show transformation centers", show_centers, "Mark verticals from the UAS on the ground.");
// gd.addCheckbox ("Show statistics for ortho images", show_map_stats, "Generate and show statistics for ortho maps.");
gd.addCheckbox ("Show combo maps/stats", show_combo, "Generate/save combo maps and stats.");
gd.addCheckbox ("Create overlap pairs", create_overlaps, "Create scene pairs overlaps.");
gd.addCheckbox ("Create overlap pairs", create_pairwise_matches, "Create scene pairs overlaps.");
gd.addCheckbox ("Create pairwise affines", create_pairwise_affines, "Create affines for scene pairs.");
gd.addCheckbox ("Equalize overlap pairs", equalize_overlaps, "Equalize intensities in overlaps.");
gd.addCheckbox ("Create map", create_map, "Create combined map from pairwise matches.");
gd.addCheckbox ("Equalize intensities", create_equalize, "Create map intensities equalization from pairwise matches.");
......@@ -257,9 +263,14 @@ public class ComboMatch {
// update_kernel_patterns
gd.showDialog();
if (gd.wasCanceled()) return false;
int choice_index = gd.getNextChoiceIndex();
String files_list_path = files_lists_paths[choice_index]+".list";
String orthoMapsCollection_path =files_lists_paths[choice_index]+".data";
// int choice_index = gd.getNextChoiceIndex();
// String files_list_path = FILES_LISTS_PATHS[choice_index]+".list";
// String orthoMapsCollection_path =FILES_LISTS_PATHS[choice_index]+".data";
omtch_img_set = ComboMatch.FILES_LISTS_PATHS[gd.getNextChoiceIndex()];
String files_list_path = omtch_img_set+".list";
String orthoMapsCollection_path =omtch_img_set+".data";
use_saved_collection = gd.getNextBoolean();
save_collection = gd.getNextBoolean();
String orthoMapsCollection_savepath = save_collection?orthoMapsCollection_path:null;
......@@ -289,7 +300,8 @@ public class ComboMatch {
OrthoMap.setGPUWidthHeight(gpu_width,gpu_height);
show_combo = gd.getNextBoolean();
create_overlaps = gd.getNextBoolean();
create_pairwise_matches = gd.getNextBoolean();
create_pairwise_affines = gd.getNextBoolean();
equalize_overlaps = gd.getNextBoolean();
create_map = gd.getNextBoolean();
create_equalize = gd.getNextBoolean();
......@@ -753,7 +765,9 @@ public class ComboMatch {
// String [] gpu_spair = {names[gpu_ipair[0]],names[gpu_ipair[1]]};
int [] origin = new int[2];
if (show_combo) { // now all modes, and stats?
maps_collection.processComboMap(debugLevel);
maps_collection.processComboMap(
clt_parameters, // CLTParameters clt_parameters,
debugLevel);
return true;
}
......@@ -790,11 +804,22 @@ public class ComboMatch {
orthoMapsCollection_savepath); // String orthoMapsCollection_path
return (ok >= 0);
}
if (create_pairwise_matches) {
boolean ok =maps_collection.getOverlapPairs(
clt_parameters, // CLTParameters clt_parameters,
orthoMapsCollection_savepath); // String orthoMapsCollection_path);
return ok; // Just exit, do not try other commands. if (!ok) return false;
}
if (create_overlaps) {
boolean ok =maps_collection.getIntersectedPairs(
if (create_pairwise_affines) {
// boolean ok =maps_collection.getIntersectedPairs(
// clt_parameters, // CLTParameters clt_parameters,
// orthoMapsCollection_savepath); // String orthoMapsCollection_path);
boolean ok =maps_collection.generatePairwiseAffines(
clt_parameters, // CLTParameters clt_parameters,
orthoMapsCollection_savepath); // String orthoMapsCollection_path);
return ok; // Just exit, do not try other commands. if (!ok) return false;
}
if (equalize_overlaps) {
......@@ -1005,7 +1030,7 @@ public class ComboMatch {
if (process_correlation) {
if (update_match) {
pairwiseOrthoMatch.zoom_lev = zoom_lev;
pairwiseOrthoMatch.affine = affines[1];
pairwiseOrthoMatch.setAffine(affines[1]);
maps_collection.ortho_maps[gpu_pair[0]].setMatch(
maps_collection.ortho_maps[gpu_pair[1]].getName(),
pairwiseOrthoMatch);
......@@ -1272,6 +1297,13 @@ public class ComboMatch {
}
for (int i = 0; i < filenames.length; i++) {
maps_collection.ortho_maps[i].setFileName(filenames[i]);
String path = maps_collection.ortho_maps[i].getPath();
if (!(new File(path)).exists()) {
System.out.println("updateBlBcFileNames() does not exist - fix manually: "+path);
System.out.println("This can happen if files have different resolution for new run.");
System.out.println("Verify path, then modify filenames[i], break at line above: 'maps_collection.ortho_maps[i].setFileName(filenames[i])'.");
System.out.println("Reduce i by 1 and continue");
}
}
return true;
}
......@@ -1482,16 +1514,22 @@ adjusted affines[1] for a pair: 1694564291_293695/1694564778_589341
points_xy[nn][1] = fp.ypoints[p];
pair_slices[nn] = pRoi.getPointPosition(p); // works
}
PairwiseOrthoMatch match= new PairwiseOrthoMatch();
match.affine = new double[][] {
// PairwiseOrthoMatch match= new PairwiseOrthoMatch();
double [][] affine = {
{1,0,(points_xy[1][0]-points_xy[0][0])*pix_size},
{0,1,(points_xy[1][1]-points_xy[0][1])*pix_size}};
PairwiseOrthoMatch match= new PairwiseOrthoMatch(
affine, // double [][] affine,
new double [6][6], // double [][] jtj,
Double.NaN, // double rms,
zoom_level, // int zoom_lev,
1.0); // double overlap);
for (int n = 0; n < points_xy.length; n++) {
names[n] = stack_scenes.getSliceLabel(pair_slices[n]).substring(0,17);
}
System.out.println("names = [\""+names[0]+"\", \""+names[1]+"\"]");
System.out.println("match.affine = [["+match.affine[0][0]+", "+match.affine[0][1]+", "+match.affine[0][2]+"],");
System.out.println(" ["+match.affine[1][0]+", "+match.affine[1][1]+", "+match.affine[1][2]+"]]");
System.out.println("match.affine = [["+affine[0][0]+", "+affine[0][1]+", "+affine[0][2]+"],");
System.out.println(" ["+affine[1][0]+", "+affine[1][1]+", "+affine[1][2]+"]]");
return match;
}
}
......
......@@ -216,9 +216,33 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{
pairwise_matches.remove(name);
}
public PairwiseOrthoMatch getMatch(
String name,
boolean with_undefined) {
PairwiseOrthoMatch match = pairwise_matches.get(name);
if (match == null) return null;
if (with_undefined || match.isDefined()) return match;
return null;
}
public PairwiseOrthoMatch getMatch(String name) {
return pairwise_matches.get(name);
return getMatch(name, false); // old - do not count undefined
// return pairwise_matches.get(name);
}
public void unsetMatches(boolean undefined_only) {
if (undefined_only) {
String [] matches = pairwise_matches.keySet().toArray(new String[0]);
for (String match:matches) {
if (!getMatch(match).isDefined()) {
unsetMatch(match);
}
}
} else {
pairwise_matches = new HashMap<String, PairwiseOrthoMatch>();
}
}
public int getOriginalZoomLevel() {
......@@ -480,7 +504,9 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{
averageImagePixel = Double.NaN; // average image pixel value (to combine with raw)
}
public double getTimeStamp() {
return ts;
}
public LocalDateTime getLocalDateTime() {
return dt;
}
......@@ -5244,6 +5270,71 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{
return pattern;
}
/**
* Decomposing linear transform into rotation
* R1={{cos(beta),sin(beta)},{-sin(beta), cos(beta)}} ,
* non-uniform scale transform W={{w1,0}{0,w2}}, and rotation
* R2={{cos(gamma),sin(gamma)},{-sin(gamma), cos(gamma)}}
* A=R1*W*R2 using singular value decomposition
* https://math.stackexchange.com/questions/861674/decompose-a-2d-arbitrary-transform-into-only-scaling-and-rotation
* @param A - input 2x2 matrix
* @return {s,beta,w,gamma,beta+gamma}
* A = B + C
* b00= b11; c00=-c01
* b10=-b01; c10= c01
*/
public static double [] singularValueDecompose(
double [][] A) {
double a00=A[0][0],a01=A[0][1],a10=A[1][0],a11=A[1][1];
double b00=(a00+a11)/2; // , b11 = b00;
double c00=(a00-a11)/2; //, c11 =-c00;
double b01=(a01-a10)/2; //, b10 =-b01;
double c01=(a01+a10)/2; //, c10 = c01;
double w1_p_w2_2= Math.sqrt(b00*b00+b01*b01);
double w1_m_w2_2= Math.sqrt(c00*c00+c01*c01);
double w1 = w1_p_w2_2 + w1_m_w2_2;
double w2 = w1_p_w2_2 - w1_m_w2_2;
double g_p_b = Math.atan2(b01, b00);
double g_m_b = Math.atan2(c01, c00);
double gamma = (g_p_b + g_m_b)/2;
double beta = (g_p_b - g_m_b)/2;
return new double [] {beta,w1,w2,gamma,g_p_b};
}
/**
* Use singular value decomposition and then split scaling {{w1,0},{0,w1}}
* into overall scaling caused by zoom != 1.0 because of altitude error
* and unidirectional scaling caused by tilted projection plane. As the
* input linear transformation matrix converts ground coordinates to source
* image coordinates, the scale in the tilt direction is > than scale in the
* perpendicular direction (tilt axis).
* Matrix R1 is additionally rotated by PI/2 if needed so W={{w1,0},{0,w2}}
* has w2>=w1 and W={{s,0},{0,s/t}}, where t <= 1.0 and equals to cos(tilt)
*
* @param A - linear transformation matrix from rectified ground coordinates
* to source image coordinates. OK to use 2x3 affine matrix,extra
* components will be ignored.
* @return {beta, s, t, beta+gamma}, beta+gamma - total rotation
*/
public static double [] singularValueDecomposeScaleTilt(
double [][] A) {
double [] svd=singularValueDecompose(A);
double w1 = svd[1], w2 = svd[2], beta=svd[0], g_p_b=svd[4];
// considering tilt in y direction, it should have higher scale
// (and source image coordinates), while X should correspond to
// the axis of rotation, and scale is just scale caused by error in
// altitude.
double s = Math.min(w1, w2);
double t = w1/w2; // <=1.0, ==cos(tilt)
if (w1 > w2) { // rotate tilt by PI/2
t = w2/w1;
beta += Math.PI/2;
if (beta > Math.PI) {
beta -= 2* Math.PI;
}
}
return new double [] {beta, s,t, g_p_b};
}
......
......@@ -24,6 +24,7 @@ import java.util.Calendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import com.elphel.imagej.calibration.CalibrationFileManagement;
......@@ -54,6 +55,16 @@ public class OrthoMapsCollection implements Serializable{
public static final int MODE_ALT = 1;
public static final int MODE_MASK = 2;
public static final int PAIR_NONE = 0;
public static final int PAIR_DEFINED = 1;
public static final int PAIR_UNDEFINED = 2;
public static final int PAIR_FAILED = -1;
public static final int HEUR_LAST_SEQ = 1; // use last of connected following this
public static final int HEUR_DIV_LONG = 2; // divide long connected series
public static final int HEUR_MIN_DIA = 4; // minimal diameter increase (or no increase)
public static final int HEUR_SAME_DIA = 8; // minimal diameter increase (or no increase)
public static final String [] KEY_DIRS= {"rootDirectory", // from EyesisCorrectionParameters
"sourceDirectory","linkedModels","videoDirectory","x3dDirectory","resultsDirectory","scenesDirectory",
"kernelsDirectory", "patternsDirectory"};
......@@ -178,7 +189,7 @@ public class OrthoMapsCollection implements Serializable{
map_index = new HashMap<Double,Integer>();
map_index_string = new HashMap<String,Integer>();
for (int i = 0; i < ortho_maps.length; i++) {
map_index.put(ortho_maps[i].ts, i);
map_index.put(ortho_maps[i].getTimeStamp(), i);
map_index_string.put(ortho_maps[i].name,i);
}
return ortho_maps.length;
......@@ -205,6 +216,9 @@ public class OrthoMapsCollection implements Serializable{
public void updateLLa() { // make automatic by saving/testing file modification stamp?
for (int n = 0; n < ortho_maps.length; n++) {
if (n == 530) {
System.out.println("updateLLa(): n="+n);
}
ortho_maps[n].updateLLA();
}
updateAGL();
......@@ -1163,7 +1177,7 @@ public class OrthoMapsCollection implements Serializable{
ease_nosfm, // double ease_nosfm,
max_rms_iter, // double [] max_rms_iter, // = {1.0, 0.6};//
debugLevel-4); // final int debugLevel)
pairwiseOrthoMatch.affine = affines[1]; // modified by correlateOrthoPair
pairwiseOrthoMatch.setAffine(affines[1]); // modified by correlateOrthoPair
if (debugLevel > -4) {
System.out.println(String.format("SpiralMatch(): %3d-%3d nx = %3d, ny=%3d, RMSE=%8.6f",
gpu_pair[0], gpu_pair[1], nx,ny,pairwiseOrthoMatch.rms)); // if NaN - provide reason
......@@ -4036,7 +4050,11 @@ public class OrthoMapsCollection implements Serializable{
public String [] getScenesList() {
String [] lines = new String [ortho_maps.length];
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM/dd/yyyy HH:mm:ss.SS zzz");// may be VV instead of zzz
int dbg_indx = -530;
for (int indx = 0; indx < ortho_maps.length; indx++) {
if (indx == dbg_indx) {
System.out.println("indx="+indx);
}
OrthoMap map = ortho_maps[indx];
LocalDateTime ldt = map.getLocalDateTime();
ZonedDateTime zonedUTC = ldt.atZone(ZoneId.of("UTC")); // that time is UTC
......@@ -4304,6 +4322,146 @@ public class OrthoMapsCollection implements Serializable{
return true;
}
public boolean getOverlapPairs(
CLTParameters clt_parameters,
String orthoMapsCollection_path) {
int [] indices = getScenesSelection(
null, // boolean select_all,
" to find intersects"); // String purpose)
if (indices == null) {
return false;
}
int zoom_lev = clt_parameters.imp.pwise_zoom; // -5;
double min_overlap_frac = clt_parameters.imp.pwise_overlap; // 0.25;
boolean ignore_affines = false;
boolean from_scratch = false;
boolean bounds_to_indices = true;
int debugLevel = clt_parameters.imp.pwise_debug;
GenericJTabbedDialog gd = new GenericJTabbedDialog("Get scene intersections",1200,300);
gd.addNumericField("Zoom level", zoom_lev, 0,4,"",
"Zoom level: +1 - zoom in twice, -1 - zoom out twice");
gd.addNumericField("Minimal overlap", min_overlap_frac, 3,7,"",
"Minimal overlap as a fraction of the smaller image.");
gd.addCheckbox ("Ignore known affines", ignore_affines, "Ignore previously calculated scenes' affines.");
gd.addCheckbox ("Start from scratch", from_scratch, "Remove all pairwise affines (false - keep them).");
gd.addNumericField("Pairwise match debug level", debugLevel, 0,3,"","Debug level during Spiral search.");
gd.showDialog();
if (gd.wasCanceled()) return false;
zoom_lev = (int) gd.getNextNumber();
min_overlap_frac = gd.getNextNumber();
ignore_affines = gd.getNextBoolean();
from_scratch = gd.getNextBoolean();
debugLevel= (int) gd.getNextNumber();
int [] wh = new int[2];
int [] origin = new int[2];
double [][] centers =new double [indices.length][];
double [][][] affines = null;
if (ignore_affines) {
affines = new double [indices.length][2][3];
for (int i = 0; i < affines.length; i++) {
affines[i][0][0] = 1.0;
affines[i][1][1] = 1.0;
}
}
for (int i = 0; i < indices.length; i++) {
// public void unsetMatches(boolean undefined_only) {
ortho_maps[indices[i]].unsetMatches(!from_scratch);
}
double [][] dmulti = renderMultiDouble (
null, // double [][] ground_planes, // null - images, non-null altitudes. use new double[2][3] for old way alt
indices, // int [] indices, // null or which indices to use (normally just 2 for pairwise comparison)
bounds_to_indices, // boolean bounds_to_indices,
affines, // affines, // double [][][] affines, // null or [indices.length][2][3]
null, // double [][] equalize,
true, // boolean ignore_equalize,
null, // warp, // FineXYCorr warp,,
zoom_lev, // int zoom_level,
wh, // int [] wh,
origin, // int [] origin){ // maps[0] as a reference
centers); // double [][] centers)
double [] overlaps = getFracOverlaps(
dmulti); // final double [][] dmulti) {
int num_overlaps_all = 0, num_overlaps_sel = 0;
boolean [][] intersects = new boolean [dmulti.length][dmulti.length];
for (int i = 0; i < dmulti.length-1; i++) {
for (int j = i+1; j < dmulti.length; j++) {
int indx = i*dmulti.length+j;
double overlap = overlaps[indx];
if (overlap > 0) {
num_overlaps_all++;
if (overlap > min_overlap_frac) {
String name2 = ortho_maps[indices[j]].getName();
PairwiseOrthoMatch match = ortho_maps[indices[i]].getMatch(name2);
if (match == null) {
match = new PairwiseOrthoMatch (
null, // double [][] affine,
new double [6][6], // double [][] jtj,
Double.NaN, // double rms,
zoom_lev, // int zoom_lev,
overlap); // double overlap)
ortho_maps[indices[i]].setMatch(name2, match);
} else {
match.setOverlap(overlap); // just update overlap
}
num_overlaps_sel++;
intersects[i][j] = true;
}
}
}
}
int [] groups = new int [dmulti.length];
for (int i = 0; i < groups.length; i++) {
groups[i] = i;
}
for (int i = 0; i< (groups.length-1); i++) {
for (int j = i+1; j < groups.length; j++) if (intersects[i][j] && (groups[j] != groups[i])){
int g0 = groups[i];
int g1 = groups[j];
for (int k = 0; k < groups.length; k++) {
if (groups[k] == g1) {
groups[k] = g0;
}
}
}
}
HashSet<Integer> hs = new HashSet<Integer>();
for (int i:groups) {
hs.add(i);
}
System.out.println("getIntersectedPairs(): num_overlaps_all="+num_overlaps_all+
", num_overlaps_sel="+num_overlaps_sel+", number of disconnected groups="+hs.size());
if (debugLevel > -4) {
ShowDoubleFloatArrays.showArrays(
overlaps,
"Overlaps");
}
if (orthoMapsCollection_path != null) {
try {
writeOrthoMapsCollection(orthoMapsCollection_path);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (debugLevel > -4) {
System.out.println("Saved data to "+ orthoMapsCollection_path);
}
}
return true;
}
public boolean getIntersectedPairs(
CLTParameters clt_parameters,
String orthoMapsCollection_path) {
......@@ -4396,12 +4554,14 @@ public class OrthoMapsCollection implements Serializable{
return matches_ok;
}
public boolean generatePairwiseMatches(
CLTParameters clt_parameters,
double min_overlap_frac,
int [] indices,
double [] overlaps,
// boolean [][] boverlaps,
String orthoMapsCollection_path) {
// gen number of pairs:
int num_scenes = indices.length;
......@@ -4896,8 +5056,674 @@ public class OrthoMapsCollection implements Serializable{
return true;
}
public boolean generatePairwiseAffines(
CLTParameters clt_parameters,
String orthoMapsCollection_path) {
int [] indices = getScenesSelection(
null, // boolean select_all,
" to find intersects"); // String purpose)
if (indices == null) {
return false;
}
// gen number of pairs:
// int num_scenes = indices.length;
int [][] pair_state = new int [indices.length][indices.length];
double [][] overlaps = new double [indices.length][indices.length];
double [] timestamps = new double [indices.length];
int [] groups = new int [indices.length];
int [][] dist = new int [indices.length][indices.length];
for (int i = 0; i < indices.length; i++) {
groups[i] = i;
timestamps[i] = ortho_maps[indices[i]].getTimeStamp();
}
// int num_pairs = 0;
int num_defined = 0;
int num_undefined = 0;
for (int i = 0; i < indices.length-1; i++) {
for (int j = i+1; j < indices.length; j++){
String name2 = ortho_maps[indices[j]].getName();
PairwiseOrthoMatch match = ortho_maps[indices[i]].getMatch(name2,true);
if (match != null) {
if (match.isDefined()) {
pair_state[i][j] = PAIR_DEFINED;
num_defined++;
/*
// update groups
if (groups[i] != groups[j]) {
int g0 = Math.min(groups[i], groups[j]);
int g1 = Math.max(groups[i], groups[j]);
for (int k = 0; k < groups.length; k++) {
if (groups[k] == g1) {
groups[k] = g0;
}
}
}
updateDistance(
new int [] {i,j}, // final int [] pair,
dist); // final int [][] dist)
*/
recordPair(
new int [] {i,j}, // int [] pair,
true, // boolean success,
pair_state, //int [][] pair_state,// = new int [indices.length][indices.length];
groups, // int [] groups, // = new int [indices.length];
dist); //int [][] dist) // = new int [indices.length];
} else {
pair_state[i][j] = PAIR_UNDEFINED;
num_undefined++;
}
overlaps[i][j] = match.getOverlap();
}
}
}
HashSet<Integer> hs = new HashSet<Integer>();
for (int i:groups) {
hs.add(i);
}
boolean dry_run = false;
boolean skip_exist = clt_parameters.imp.pwise_skip_exist; //
boolean refine_exist = clt_parameters.imp.pwise_refine_exist; // if false, start from scratch, true - start from previous
boolean delete_failed = clt_parameters.imp.pwise_delete_fail; // delete existing match if now failed
boolean gen_inverse = clt_parameters.imp.pwise_gen_inverse; // generate inverse matches
boolean save_each = clt_parameters.imp.pwise_save_each; // save state file after each match
boolean log_append = clt_parameters.imp.pwise_log_append; //
String log_path = clt_parameters.imp.pwise_log_path; //
int debugLevel = clt_parameters.imp.pwise_debug; //
//Initial spiral search for image matching
double search_step = clt_parameters.imp.ospir_step; // 8.0; // pix
double search_range = clt_parameters.imp.ospir_range; // 50.0; // pix
double double_threshold = clt_parameters.imp.ospir_double; //
double good_rms = clt_parameters.imp.ospir_good_rms; // 0.27; //
double max_rms = clt_parameters.imp.ospir_max_rms; // 0.35; //
int min_overlap = clt_parameters.imp.ospir_overlap; // 3000; // do not try to match if there is too small overlap (scaled pixels)
int num_iter_lma = clt_parameters.imp.ospir_num_iter; // 5;
double [] max_rms_iter = clt_parameters.imp.ospir_rms_iter; // {1.0, 0.6};//
boolean spiral_ignore_rms = clt_parameters.imp.ospir_ignore_rms; // false
int spiral_debug = clt_parameters.imp.ospir_debug; // 0;
//Final pairwise scenes matching
double frac_remove = clt_parameters.imp.pmtch_frac_remove;// 0.15;
double metric_err = clt_parameters.imp.pmtch_metric_err;// 0.05; // 0.02;// 2 cm
boolean pmtch_use_affine = clt_parameters.imp.pmtch_use_affine;
double max_std = clt_parameters.imp.pmtch_max_std;// 1.5; // maximal standard deviation to limit center area
double min_std_rad = clt_parameters.imp.pmtch_min_std_rad;// 2.0; // minimal radius of the central area (if less - fail)
boolean ignore_prev_rms = clt_parameters.imp.pmtch_ignore_rms;// true;
int num_tries = clt_parameters.imp.pmtch_num_iter;// 10;
double rad_fraction = clt_parameters.imp.pmtch_cent_rad; // center circle radius fraction of 0.5* min(width, height) in tiles
double max_tile_rad = clt_parameters.imp.pmtch_max_cent_rad;// maximal center radius in tiles (limit pmtch_cent_rad)
double fill_fraction = clt_parameters.imp.pmtch_cent_fill; // should be populated not less than this
double fill_fraction_final = clt_parameters.imp.pmtch_cent_final; // should be populated not less than this during final pass
double ease_nosfm = clt_parameters.imp.pmtch_ease_nosfm; // ease metric_error when no SfM gain == 0;
boolean use_multi = true;
int heur = 15;
int min_scene = 0;
GenericJTabbedDialog gd = new GenericJTabbedDialog("Pairwise Match Parameters",1200,1000);
gd.addMessage("Number of scenes - "+indices.length+
", number of defined pairs - "+num_defined+
", number of undefined pairs - "+num_undefined+
", number of disconnected groups - "+hs.size());
//
gd.addCheckbox ("Dry run", dry_run, "Create pairs, do not adjust.");
gd.addCheckbox ("Skip existing", skip_exist, "Do not regenerate if match with same or higher resolution exists.");
gd.addCheckbox ("Refine existing", refine_exist, "Refine existing matches (false - start from scratch with spiral search).");
gd.addCheckbox ("Delete failed", delete_failed, "Delete previous matches if it failed now.");
gd.addCheckbox ("Generate inverse matches", gen_inverse, "Generate (refine if exist and enabled) inverse matches.");
gd.addCheckbox ("Save state after each match", save_each, "Update state file after each match generation to mitigate possible crashes.");
gd.addCheckbox ("Write log file", log_append, "Enable writing log file with matching results.");
gd.addStringField ("Log file full path", log_path, 150, "Path of the log file to be appended.");
gd.addNumericField("Pairwise match debug level", debugLevel, 0,3,"","Debug level during Spiral search.");
gd.addMessage ("Initial spiral search for image matching");
gd.addNumericField("Spiral search step", search_step, 3,7,"scaled pix", "Distance between spiral search probes, in scaled pixels.");
gd.addNumericField("Spiral search radius", search_range, 3,7,"scaled pix", "Maximal radius of the spiral search, in scaled pixels.");
gd.addNumericField("Mitigate small overlap", double_threshold, 3,7,"","For small overlaps increase zoom by 1 and range - twice.");
gd.addNumericField("RMSE to end search", good_rms, 3,7,"scaled pix", "Maximal RMSE to consider match, in scaled pixels.");
gd.addNumericField("Satisfactory RMSE", max_rms, 3,7,"scaled pix", "Maximal RMSE to consider match, in scaled pixels.");
gd.addNumericField("Minimal overlap", min_overlap, 0,4,"scaled pix ^ 2","Minimal overlap area in square scaled pixels.");
gd.addNumericField("LMA iterations", num_iter_lma, 0,2,"", "Number of LMA iterations during spiral search.");
gd.addNumericField("RMSE at first iteration", max_rms_iter[0], 3,7,"scaled pix","Maximal RMSE at first iteration.");
gd.addNumericField("RMSE at second iteration", max_rms_iter[1], 3,7,"scaled pix","Maximal RMSE at second iteration.");
gd.addCheckbox ("Ignore worsening RMSE", spiral_ignore_rms, "Ignore worsening/not improving RMSE during spiral search.");
gd.addNumericField("Spiral search debug level", spiral_debug, 0,3,"","Debug level during Spiral search.");
gd.addMessage ("Final pairwise scenes matching");
gd.addNumericField("Remove fraction of worst matches", frac_remove, 3,7,"", "When fitting scenes remove this fraction of worst match tiles.");
gd.addNumericField("Maximal metric error", metric_err, 3,7,"m", "Maximal tolerable fitting error caused by elevation variations.");
gd.addCheckbox ("Use scenes' affine", pmtch_use_affine, "Use known scenes' affine matrices, false - start from scratch (unity) ones.");
gd.addNumericField("Central area standard deviation", max_std, 3,7,"", "Central area limit by the standard deviation.");
gd.addNumericField("Central area minimal radius", min_std_rad, 3,7,"tile", "Minimal radius of the central area after all LMA passes.");
gd.addCheckbox ("Ignore previous RMSE", ignore_prev_rms, "Do not exit full fitting cycles if the RMSE worsened/not improved.");
gd.addNumericField("Number of fitting iterations", num_tries, 0,3,"","number of full fittng iterations.");
gd.addNumericField("Central area radius as fraction", rad_fraction, 3,7,"", "Central area radius as fraction of half minimal WOI dimension.");
gd.addNumericField("Maximal central area radius", max_tile_rad, 3,7,"tiles", "Absolute limit to the center area radius (eases bad peripheral matching).");
gd.addNumericField("Central area minimal fill", fill_fraction, 3,7,"", "Central area minimal fill for all but the last iteration.");
gd.addNumericField("Central area minimal fill final", fill_fraction_final, 3,7,"", "Central area minimal fill for the last iteration.");
gd.addNumericField("Relax metric error for no-SfM", ease_nosfm, 3,7,"", "Relax metric error for no-SfM scenes (sfm_gain==0).");
gd.addNumericField("Start scene (skip all earlier)", min_scene, 0,3,"","To be able to continue skipping some.");
gd.addNumericField("Heuristics bitmap", heur, 0,3,"","Bitmap of modes to suggest the next pair.");
gd.addCheckbox ("Use multiple threads", use_multi, "Use multipl tghreads (may be disabled in debug mode).");
//
gd.showDialog();
if (gd.wasCanceled()) return false;
dry_run = gd.getNextBoolean();
skip_exist = gd.getNextBoolean();
refine_exist = gd.getNextBoolean();
delete_failed = gd.getNextBoolean();
gen_inverse = gd.getNextBoolean();
save_each = gd.getNextBoolean();
log_append = gd.getNextBoolean();
log_path = gd.getNextString();
debugLevel = (int) gd.getNextNumber();
search_step = gd.getNextNumber();
search_range = gd.getNextNumber();
double_threshold = gd.getNextNumber();
good_rms = gd.getNextNumber();
max_rms = gd.getNextNumber();
min_overlap = (int) gd.getNextNumber();
num_iter_lma = (int) gd.getNextNumber();
max_rms_iter[0] = gd.getNextNumber();
max_rms_iter[1] = gd.getNextNumber();
spiral_ignore_rms= gd.getNextBoolean();
spiral_debug = (int) gd.getNextNumber();
frac_remove = gd.getNextNumber();
metric_err = gd.getNextNumber();
pmtch_use_affine= gd.getNextBoolean();
max_std = gd.getNextNumber();
min_std_rad = gd.getNextNumber();
ignore_prev_rms = gd.getNextBoolean();
num_tries = (int) gd.getNextNumber();
rad_fraction = gd.getNextNumber();
max_tile_rad = gd.getNextNumber();
fill_fraction = gd.getNextNumber();
fill_fraction_final= gd.getNextNumber();
ease_nosfm = gd.getNextNumber();
min_scene = (int) gd.getNextNumber();
heur = (int) gd.getNextNumber();
use_multi = gd.getNextBoolean();
if (dry_run) {
PairsGraph pairsGraph = new PairsGraph(
this, // OrthoMapsCollection orthoMapsCollection,
indices, // int [] indices,
use_multi, // boolean multi,
debugLevel); // int debugLevel);
return pairsGraph.dryRun(
heur, // int heur,
debugLevel); // int debugLevel)
/*
int [][] pairs = getPairsIndices( // maybe update sometimes?
pair_state, // final int [][] pair_state,
groups); // final int [] groups);
*
int num_compact = 10; // compact after each 10 runs
int num_pairs = 0;
int ok_dist = 1;
int [] start_ij = {0,0};
int [] next_pair;
do {
next_pair = suggestPairSingle( // single-connected, no cycles
// add some parameters
heur, // int heur,
pairs, // int [][] pairs,
pair_state, // int [][] pair_state, // = new int [indices.length][indices.length];
overlaps, // double [][] overlaps, // = new double [indices.length][indices.length];
timestamps, // double [] timestamps, // = new double [indices.length];
groups, // int [] groups, // = new int [indices.length];
dist, // int [][] dist) { // = new int [indices.length];
ok_dist, // int ok_dist)
start_ij); // int [] start_ij) // = new int [indices.length];
if (next_pair == null) {
break;
}
if (next_pair[2] > HEUR_LAST_SEQ) {
heur &= ~HEUR_LAST_SEQ;
}
if (next_pair[2] > HEUR_DIV_LONG) {
heur &= ~HEUR_DIV_LONG;
}
if (next_pair.length > 3) {
ok_dist = next_pair[3];
start_ij[0] = next_pair[0];
start_ij[1] = next_pair[1];
System.out.println(String.format("%3d: %3d-%3d %2d (%3d)", num_pairs,next_pair[0], next_pair[1],next_pair[2],next_pair[3]));
} else {
System.out.println(String.format("%3d: %3d-%3d %2d", num_pairs,next_pair[0], next_pair[1],next_pair[2]));
}
recordPair(
next_pair, // int [] pair,
true, // boolean success,
pair_state, // int [][] pair_state,// = new int [indices.length][indices.length];
groups, // int [] groups, // = new int [indices.length];
dist); // int [][] dist) // = new int [indices.length];
*/
/*
if ((num_pairs % num_compact) == 0) {
pairs = updatePairsIndices(
true, // final boolean compact,
pairs, // final int [][] pairs,
groups); // final int [] groups)
} else {
updatePairsIndices(
false, // final boolean compact,
pairs, // final int [][] pairs,
groups); // final int [] groups)
}
*/
/*
num_pairs++;
} while (true);
String [] titles = {"overlap", "state", "dist"};
double [][] dbg_img = new double [titles.length][indices.length*indices.length];
for (int i = 0; i < indices.length; i++) {
for (int j = 0; j < indices.length; j++) {
int indx = i * indices.length+ j;
dbg_img[0][indx] = overlaps[i][j];
dbg_img[1][indx] = pair_state[i][j];
dbg_img[2][indx] = dist[i][j];
}
}
ShowDoubleFloatArrays.showArrays(
dbg_img,
indices.length,
indices.length,
true,
"dry_run.tiff",
titles);
*/
// return true;
}
/*
return generatePairwiseMatches(
clt_parameters, // CLTParameters clt_parameters,
min_overlap_frac, // double min_overlap_frac,
indices, // int [] indices,
overlaps, // double [] overlaps,
skip_exist, // boolean skip_existing,
refine_exist, // boolean refine_existing, // if false, start from scratch, true - start from previous
delete_failed, // boolean delete_failed, // if false, start from scratch, true - start from previous
gen_inverse, // boolean gen_inverse, // generate inverse matches
save_each, // boolean save_each, // save state file after each match
log_append, // boolean log_append, //
log_path, // String log_path,
orthoMapsCollection_path, // String orthoMapsCollection_path
search_step, // double search_step,
search_range, // double search_range,
good_rms, // double good_rms,
max_rms, // double max_rms,
min_overlap, // int min_overlap,
num_iter_lma, // int num_iter_lma,
spiral_ignore_rms, // boolean ignore_rms,
spiral_debug, // int spiral_debug,
frac_remove, // double frac_remove,
metric_err, // double metric_error,
pmtch_use_affine, // boolean pmtch_use_affine,
max_std, // double max_std,
min_std_rad, // double min_std_rad,
ignore_prev_rms, // boolean ignore_prev_rms,
num_tries, // int num_tries,
rad_fraction, // double rad_fraction,
max_tile_rad, // double max_tile_rad,
fill_fraction, // double fill_fraction,
fill_fraction_final, // double fill_fraction_final,
ease_nosfm, // double ease_nosfm,
double_threshold, // double double_threshold,
max_rms_iter, // double [] max_rms_iter,
min_scene, // int min_scene,
debugLevel); // int debugLevel
*/
return true;
}
public static int [] suggestPairSingle( // single-connected, no cycles
// add some parameters
final int heur,
final int [][] pairs,
final int [][] pair_state, // = new int [indices.length][indices.length];
final double [][] overlaps, // = new double [indices.length][indices.length];
final double [] timestamps, // = new double [indices.length];
final int [] groups, // = new int [indices.length];
final int [][] dist,
final int ok_dist,
final int [] start_ij) { // = new int [indices.length];
int i0 = 1;
single: {
int g0 = groups[0];
for (; i0 < groups.length; i0++) if (groups[i0] !=g0){
break single;
}
return null; // already single-connected
}
int i = i0-1;
// groups[i] - last same group as group[0]
if ((heur & HEUR_LAST_SEQ) != 0) {// try last in connected sequence - it should not yet be the same group
for (; i < (groups.length-1); i++) {
if (groups[i+1] != groups[i]) {
if ((pair_state[i][i+1] != 0) && ((i == 0) || (pair_state[i-1][i] == 0))) { // next is somehow connected, but previous is not
int j = i+1;
for (; (j < groups.length) && (pair_state[i][j] != 0); j++);
j--; // now j - last connected. Go backward if last connected failed
for (; j > i; j--) {
if (pair_state[i][j] != PAIR_FAILED) {
if (groups[j] != groups[i]) {
return new int [] {i,j,HEUR_LAST_SEQ};
}
break; // for (; j > i; j--) { : already connected
}
}
}
}
}
}
if ((heur & HEUR_DIV_LONG) != 0) {// Try middle of the long connected series
i = i0-1;
for (; i < (groups.length-1); i++) {
if (groups[i+1] != groups[i]) {
// if (pair_state[i][i+1] != 0) { // next is somehow connected
if ((pair_state[i][i+1] != 0) && ((i == 0) || (pair_state[i-1][i] == 0))) { // next is somehow connected, but previous is not
int j = i+1;
int num_cand=0;
for (; (j < groups.length) && (pair_state[i][j] != 0) && (groups[j] != groups[i]); j++) {
if (pair_state[i][j] == PAIR_UNDEFINED) num_cand++;
}
if (num_cand > 0) {
j--; // now j - last connected. Go backward if last connected failed
// find the middle
int kbest = -1;
double best = -1;
for (int k = i+1; k <= j; k++) if (pair_state[i][k] == PAIR_UNDEFINED){
double d = Math.min(timestamps[k]-timestamps[i], timestamps[j]-timestamps[k]);
if (d > best) {
kbest = k;
best = d;
}
}
return new int [] {i,kbest,HEUR_DIV_LONG};
}
}
}
}
}
if ((heur & (HEUR_MIN_DIA | HEUR_SAME_DIA)) != 0) { // minimal diameter increase (or no increase)
// use pairs, make multithreaded (separate method)
final boolean [] tried = new boolean [pairs.length];
final Thread[] threads = ImageDtt.newThreadArray();
final AtomicInteger ai = new AtomicInteger(0);
// final AtomicInteger astart= new AtomicInteger(0);
final AtomicInteger abest_dia= new AtomicInteger(pair_state.length);
final AtomicBoolean ago = new AtomicBoolean(true);
final int [][] best_row_col= new int [pair_state.length+1][];
final boolean use_min_dia = (heur & HEUR_MIN_DIA) != 0;
final boolean use_same_dia = (heur & HEUR_SAME_DIA) != 0;
final int [] same_row_col = {-1,-1};
// find minimal diameter or first that does not increase it
for (int pass = 0; pass <2; pass++) {
if (pass == 0) {
for (int n = 0; n < pairs.length; n++) if ((pairs[n] != null) && (pairs[n][0]>=start_ij[0])){
if ((pairs[n][0] >start_ij[0]) || (pairs[n][1] > start_ij[1])) {
ai.set(n);
break;
}
}
} else {
ai.set(0); // start from the very beginning on pass 1
}
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() {
public void run() {
for (int n = ai.getAndIncrement(); n < pairs.length; n = ai.getAndIncrement()) if (ago.get() && (pairs[n] != null) && !tried[n]){
tried[n] = true; // for the second pass
int row = pairs[n][0];
int col = pairs[n][1];
if ((pair_state[row][col] == PAIR_UNDEFINED) && (groups[row] != groups[col])) {
int [] dias = newDiameter( // or return a pair - new/old?
pairs[n], // final int [] pair,
dist); // final int [][] dist)
int d = dias[0];
if (use_min_dia && (d <= ok_dist)) {
ago.set(false); // got it, other threads may finish
}
int was_min = abest_dia.getAndAccumulate(dias[0], Math::min);
if (was_min > d) { // I set it
best_row_col[d] = pairs[n];
}
if (use_same_dia && (dias[0] <= dias[1])) {
boolean i_stopped = ago.getAndSet(false);
if (i_stopped) {
same_row_col[0] = pairs[n][0];
same_row_col[1] = pairs[n][1];
}
}
}
}
}
};
}
ImageDtt.startAndJoin(threads);
if (!ago.get()) { // was stopped after condition met (on any pass)
if (same_row_col[0] >= 0) {
return new int [] {same_row_col[0],same_row_col[1], HEUR_SAME_DIA};
} else {
int [] row_col = best_row_col[abest_dia.get()];
return new int [] {row_col[0],row_col[1], HEUR_MIN_DIA, ok_dist}; // keep same distance
}
}
} // for (int pass = 0; pass <2; pass++)
if (use_min_dia) {
int [] row_col = best_row_col[abest_dia.get()];
return new int [] {row_col[0],row_col[1], HEUR_MIN_DIA, abest_dia.get()}; // update (increase) best distance
}
}
// just any? Or it is not needed after (heur & HEUR_MIN_DIA)?
return null;
}
public static int [][] updatePairsIndices(
final boolean compact,
final int [][] pairs,
final int [] groups){
final Thread[] threads = ImageDtt.newThreadArray();
final AtomicInteger ai = new AtomicInteger(0);
final AtomicInteger aindx = new AtomicInteger(0);
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() {
public void run() {
for (int n = ai.getAndIncrement(); n < pairs.length; n = ai.getAndIncrement())if (pairs[n] != null){
if (groups[pairs[n][0]] == groups[pairs[n][1]]) {
pairs[n] = null;
aindx.getAndIncrement();
}
}
}
};
}
ImageDtt.startAndJoin(threads);
if (compact) {
final int [][] pairs_compact = new int [pairs.length - aindx.get()][];
ai.set(0);
aindx.set(0);
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() {
public void run() {
for (int n = ai.getAndIncrement(); n < pairs.length; n = ai.getAndIncrement()) if (pairs[n] != null){
pairs_compact[aindx.getAndIncrement()] = pairs[n];
}
}
};
}
ImageDtt.startAndJoin(threads);
return pairs_compact;
}
return pairs;
}
public static int [][] getPairsIndices(
final int [][] pair_state,
final int [] groups){
final int n = pair_state.length;
final int n2 = n * n;
final int [][] pairs0 = new int[n * (n-1) / 2][];
final Thread[] threads = ImageDtt.newThreadArray();
final AtomicInteger ai = new AtomicInteger(0);
final AtomicInteger aindx = new AtomicInteger(0);
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() {
public void run() {
for (int i = ai.getAndIncrement(); i < n2; i = ai.getAndIncrement()){
int row = i / n;
int col = i % n;
if ((col > row ) && (groups[col] != groups[row]) && (pair_state[row][col] == PAIR_UNDEFINED)) {
int indx = aindx.getAndIncrement();
pairs0[indx] = new int[] {row,col};
}
}
}
};
}
ImageDtt.startAndJoin(threads);
ai.set(0);
final int [][] pairs = new int [aindx.get()][2];
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() {
public void run() {
for (int i = ai.getAndIncrement(); i < pairs.length; i = ai.getAndIncrement()){
pairs[i][0] = pairs0[i][0];
pairs[i][1] = pairs0[i][1];
}
}
};
}
ImageDtt.startAndJoin(threads);
return pairs;
}
public static void recordPair(
int [] pair,
boolean success,
int [][] pair_state,// = new int [indices.length][indices.length];
int [] groups, // = new int [indices.length];
int [][] dist) { // = new int [indices.length];
int i = pair[0], j = pair[1];
if (success) {
pair_state[i][j] = PAIR_DEFINED;
// merge groups
if (groups[i] != groups[j]) {
int g0 = Math.min(groups[i], groups[j]);
int g1 = Math.max(groups[i], groups[j]);
for (int k = 0; k < groups.length; k++) {
if (groups[k] == g1) {
groups[k] = g0;
}
}
}
updateDistance(
pair, // final int [] pair,
dist); // final int [][] dist)
} else {
pair_state[i][j] = PAIR_FAILED;
}
}
public static void updateDistance(
final int [] pair,
final int [][] dist) { // symmetrical
final int i = pair[0], j = pair[1];
final int [][] dist2 = {dist[i].clone(),dist[j].clone()};
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() {
// pair should be disconnected, so n from one thread != m from the other
for (int n = ai.getAndIncrement(); n < dist.length; n = ai.getAndIncrement()) if ((dist2[0][n] > 0) || (n == i)){
int d1 = dist2[0][n]+1;
for (int m = 0; m < dist.length; m++) if ((dist2[1][m] > 0) || ((m == j) && (n != i))){
int d = d1+ dist2[1][m];
dist[n][m] = d;
dist[m][n] = d;
}
}
}
};
}
ImageDtt.startAndJoin(threads);
dist[i][j] = 1;
dist[j][i] = 1;
return;
}
public static int [] newDiameter( // or return a pair - new/old?
final int [] pair,
final int [][] dist) { // symmetrical
final int i = pair[0], j = pair[1];
final Thread[] threads = ImageDtt.newThreadArray();
final AtomicInteger ai = new AtomicInteger(0);
final AtomicInteger amax_new = new AtomicInteger(0);
final AtomicInteger amax_old = new AtomicInteger(0);
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() {
public void run() {
for (int n = ai.getAndIncrement(); n < dist.length; n = ai.getAndIncrement()) if ((dist[i][n] > 0) || (n==i)){
int d1 = dist[i][n]+1;
amax_new.getAndAccumulate(d1, Math::max);
int mx = d1-1;
for (int m = 0; m < dist.length; m++) if (dist[j][m] > 0){
int d = d1+ dist[j][m];
amax_new.getAndAccumulate(d, Math::max);
mx = Math.max(mx, dist[j][m]);
}
amax_old.getAndAccumulate(mx, Math::max);
}
}
};
}
ImageDtt.startAndJoin(threads);
return new int [] {amax_new.get(),amax_old.get()};
}
public static int [] newDiameterSingleThread( // or return a pair - new/old?
final int [] pair,
final int [][] dist) { // symmetrical
final int i = pair[0], j = pair[1];
int max_new = 0, max_old = 0;
for (int n = 0; n < dist.length; n++) if ((dist[i][n] > 0) || (n==i)){
int d1 = dist[i][n]+1;
max_new = Math.max(max_new, d1);
max_old = Math.max(max_old, d1-1);
for (int m = 0; m < dist.length; m++) if (dist[j][m] > 0){
int d = d1+ dist[j][m];
max_new = Math.max(max_new, d);
max_old = Math.max(max_old, dist[j][m]);
}
}
return new int [] {max_new,max_old};
}
public boolean processComboMap(
CLTParameters clt_parameters,
int debugLevel) {
int [] indices = getScenesSelection(
null, // boolean select_all,
......@@ -4907,11 +5733,15 @@ public class OrthoMapsCollection implements Serializable{
}
boolean more = true;
while (more) { // generate multiple images/stats for the same selection
more = processComboSelection(indices, debugLevel);
more = processComboSelection(
clt_parameters, // CLTParameters clt_parameters,
indices,
debugLevel);
}
return true;
}
public boolean processComboSelection(
CLTParameters clt_parameters,
int [] indices,
int debugLevel) {
boolean show_map_stats = false;
......@@ -4931,8 +5761,10 @@ public class OrthoMapsCollection implements Serializable{
boolean ignore_affines = false;
boolean ignore_equalize = false;
String save_top_dir = "/media/elphel/NVME/lwir16-proc/ortho_videos/debug/sept12-13/pattern_match/";
String sub_dir = "combo_maps";
// String save_top_dir = "/media/elphel/NVME/lwir16-proc/ortho_videos/debug/sept12-13/pattern_match/";
// String sub_dir = "combo_maps";
String save_top_dir = clt_parameters.imp.patt_save_top;
String sub_dir = clt_parameters.imp.patt_save_subdir;
boolean show_images = true;
boolean save_images = true;
......@@ -5392,10 +6224,14 @@ public class OrthoMapsCollection implements Serializable{
stats [0] = String.format(
"%3s\t%17s\t%26s\t%11s\t%11s\t%7s\t"+
"%6s\t%7s\t%6s\t%6s\t%6s\t%6s\t"+
"%6s\t%6s",
"%6s\t%6s\t"+
"%7s\t%7s\t%7s\t%7s\t%7s\t%7s\t"+
"%7s\t%7s\t%7s\t%7s",
"#", "timestamp", "date/time", "latitude", "longitude", "GND-ASL",
"AGL","pix(cm)", "width", "height","vert_x","vert_y",
"scenes", "sfm");
"scenes", "sfm",
"scale","tilt","beta","rot","offs-X","offs-Y",
"a00","a01","a10","a11");
for (int indx:indices) {
OrthoMap map = ortho_maps[indx];
LocalDateTime ldt = map.getLocalDateTime();
......@@ -5411,13 +6247,19 @@ public class OrthoMapsCollection implements Serializable{
int [] vert_pix = map.getVertPixels();
double sfm_gain = map.getSfmGain();
int scenes = map.getNumberScenes();
double [][] affine = map.getAffine();
double [] svd_st = OrthoMap.singularValueDecomposeScaleTilt(affine);
sb.append(String.format(
"%3d\t%17s\t%26s\t%11.6f\t%11.6f\t%7.2f\t"+
"%6.2f\t%7.4f\t%6d\t%6d\t%6d\t%6d\t"+
"%6d\t%6.2f\n",
"%6d\t%6.2f"+
"\t%7.4f\t%7.4f\t%7.4f\t%7.4f\t%7.3f\t%7.3f"+
"\t%7.4f\t%7.4f\t%7.4f\t%7.4f\n",
indx, name, sdt, lla[0], lla[1], lla[2] - agl,// ground level
agl,pix_size_cm, width, height,vert_pix[0],vert_pix[1],
scenes, sfm_gain));
agl, pix_size_cm, width, height, vert_pix[0], vert_pix[1],
scenes, sfm_gain,
svd_st[1],svd_st[2],svd_st[0],svd_st[3], affine[0][2], affine[1][2],
affine[0][0], affine[0][1], affine[1][0], affine[1][1]));
}
stats[1] = sb.toString();
return stats;
......
/**
**
** PairsGraph - Process graph of the pairwise connected scenes for
** the global map generation.
**
** Copyright (C) 2024 Elphel, Inc.
**
** -----------------------------------------------------------------------------**
**
** PairsGraph.java is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
** -----------------------------------------------------------------------------**
**
*/
package com.elphel.imagej.orthomosaic;
import java.util.HashSet;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import com.elphel.imagej.common.ShowDoubleFloatArrays;
import com.elphel.imagej.tileprocessor.ImageDtt;
public class PairsGraph {
public static final int PAIR_NONE = 0;
public static final int PAIR_DEFINED = 1;
public static final int PAIR_UNDEFINED = 2;
public static final int PAIR_FAILED = -1;
public static final int HEUR_LAST_SEQ = 1; // use last of connected following this
public static final int HEUR_DIV_LONG = 2; // divide long connected series
public static final int HEUR_MIN_DIA = 4; // minimal diameter increase (or no increase)
public static final int HEUR_SAME_DIA = 8; // minimal diameter increase (or no increase)
private final OrthoMapsCollection orthoMapsCollection;
private final int [] indices;
private final int [][] pair_state;
private final double [][] overlaps;
private final double [] timestamps;
private final int [] groups;
private final int [][] dist;
private int [][] pairs;
private final int debugLevel;
private boolean multi; // use single-threaded in debug mode?
public PairsGraph (
OrthoMapsCollection orthoMapsCollection,
int [] indices,
boolean multi,
int debugLevel) {
this.orthoMapsCollection = orthoMapsCollection;
this.indices = indices;
this.debugLevel = debugLevel;
this.multi = multi;
pair_state = new int [indices.length][indices.length];
overlaps = new double [indices.length][indices.length];
timestamps = new double [indices.length];
groups = new int [indices.length];
dist = new int [indices.length][indices.length];
OrthoMap [] ortho_maps=orthoMapsCollection.getMaps();
for (int i = 0; i < indices.length; i++) {
groups[i] = i;
timestamps[i] = ortho_maps[indices[i]].getTimeStamp();
}
int num_defined = 0;
int num_undefined = 0;
for (int i = 0; i < indices.length-1; i++) {
for (int j = i+1; j < indices.length; j++){
String name2 = ortho_maps[indices[j]].getName();
PairwiseOrthoMatch match = ortho_maps[indices[i]].getMatch(name2,true);
if (match != null) {
if (match.isDefined()) {
pair_state[i][j] = PAIR_DEFINED;
num_defined++;
recordPair(
new int [] {i,j}, // int [] pair,
true, // boolean success,
multi);
} else {
pair_state[i][j] = PAIR_UNDEFINED;
num_undefined++;
}
overlaps[i][j] = match.getOverlap();
}
}
}
HashSet<Integer> hs = new HashSet<Integer>();
for (int i:groups) {
hs.add(i);
}
pairs = multi? getPairsIndicesMulti():getPairsIndicesSingle();
if (debugLevel > 0) {
System.out.println("Number of scenes - "+indices.length);
System.out.println("Number of defined pairs - "+num_defined);
System.out.println("Number of undefined pairs - "+num_undefined);
System.out.println("Number of disconnected groups - "+hs.size());
}
}
public boolean dryRun(
int heur,
int debugLevel) {
int num_pairs = 0;
int ok_dist = 1;
int [] start_ij = {0,0};
int [] next_pair;
while (!isSingleConnected()) {
next_pair = suggestPairSingle( // single-connected, no cycles
// add some parameters
heur, // int heur,
ok_dist, // int ok_dist)
start_ij, // int [] start_ij) // = new int [indices.length];
multi,
debugLevel);
if (next_pair == null) {
break;
}
if (next_pair[2] > HEUR_LAST_SEQ) {
heur &= ~HEUR_LAST_SEQ;
}
if (next_pair[2] > HEUR_DIV_LONG) {
heur &= ~HEUR_DIV_LONG;
}
if (next_pair.length > 3) {
ok_dist = next_pair[3];
start_ij[0] = next_pair[0];
start_ij[1] = next_pair[1];
System.out.println(String.format("%3d: %3d-%3d %2d (%3d)", num_pairs,next_pair[0], next_pair[1],next_pair[2],next_pair[3]));
} else {
System.out.println(String.format("%3d: %3d-%3d %2d", num_pairs,next_pair[0], next_pair[1],next_pair[2]));
}
recordPair(
next_pair, // int [] pair,
true, // boolean success,
multi);
num_pairs++;
}
String [] titles = {"overlap", "state", "dist"};
double [][] dbg_img = new double [titles.length][indices.length*indices.length];
for (int i = 0; i < indices.length; i++) {
for (int j = 0; j < indices.length; j++) {
int indx = i * indices.length+ j;
dbg_img[0][indx] = overlaps[i][j];
dbg_img[1][indx] = pair_state[i][j];
dbg_img[2][indx] = dist[i][j];
}
}
ShowDoubleFloatArrays.showArrays(
dbg_img,
indices.length,
indices.length,
true,
"dry_run.tiff",
titles);
return true;
}
public int [] suggestPairSingle( // single-connected, no cycles
// add some parameters
int heur,
int ok_dist,
int [] start_ij,
boolean multi,
int debugLevel) {
int [] rslt = null;
if (isSingleConnected()) {
return null;
}
if ((heur & HEUR_LAST_SEQ) != 0) {// try last in connected sequence - it should not yet be the same group
rslt = coverLongSeq();
if (rslt != null) {
return rslt;
}
}
if ((heur & HEUR_DIV_LONG) != 0) {// Try middle of the long connected series
rslt = getMidSeq();
if (rslt != null) {
return rslt;
}
}
if ((heur & (HEUR_MIN_DIA | HEUR_SAME_DIA)) != 0) { // minimal diameter increase (or no increase)
boolean use_min_dia = (heur & HEUR_MIN_DIA) != 0;
boolean use_same_dia = (heur & HEUR_SAME_DIA) != 0;
if (multi) {
rslt = getByDiameterMulti(
use_min_dia, // final boolean use_min_dia,
use_same_dia, // final boolean use_same_dia,
start_ij, // final int [] start_ij,
ok_dist, // final int ok_dist,
debugLevel); // final int debugLevel) {
} else {
rslt = getByDiameterSingle(
use_min_dia, // final boolean use_min_dia,
use_same_dia, // final boolean use_same_dia,
start_ij, // final int [] start_ij,
ok_dist, // final int ok_dist,
debugLevel); // final int debugLevel) {
}
if (rslt != null) {
return rslt;
}
}
return rslt;
}
public boolean isSingleConnected() {
int i0 = 1;
int g0 = groups[0];
for (; i0 < groups.length; i0++) if (groups[i0] !=g0){
return false;
}
return true; // already single-connected
}
public int [] coverLongSeq() {
for (int i = 0; i < (groups.length-1); i++) {
if (groups[i+1] != groups[i]) {
if ((pair_state[i][i+1] != 0) && ((i == 0) || (pair_state[i-1][i] == 0))) { // next is somehow connected, but previous is not
int j = i+1;
for (; (j < groups.length) && (pair_state[i][j] != 0); j++);
j--; // now j - last connected. Go backward if last connected failed
for (; j > i; j--) {
if (pair_state[i][j] != PAIR_FAILED) {
if (groups[j] != groups[i]) {
return new int [] {i,j,HEUR_LAST_SEQ};
}
break; // for (; j > i; j--) { : already connected
}
}
}
}
}
return null;
}
public int [] getMidSeq() { // asymmetrical - center, then center of the first half, first quarter, ...
for (int i = 0; i < (groups.length-1); i++) {
if (groups[i+1] != groups[i]) {
if ((pair_state[i][i+1] != 0) && ((i == 0) || (pair_state[i-1][i] == 0))) { // next is somehow connected, but previous is not
int j = i+1;
int num_cand=0;
for (; (j < groups.length) && (pair_state[i][j] != 0) && (groups[j] != groups[i]); j++) {
if (pair_state[i][j] == PAIR_UNDEFINED) num_cand++;
}
if (num_cand > 0) {
j--; // now j - last connected. Go backward if last connected failed
// find the middle
int kbest = -1;
double best = -1;
for (int k = i+1; k <= j; k++) if (pair_state[i][k] == PAIR_UNDEFINED){
double d = Math.min(timestamps[k]-timestamps[i], timestamps[j]-timestamps[k]);
if (d > best) {
kbest = k;
best = d;
}
}
return new int [] {i,kbest,HEUR_DIV_LONG};
}
}
}
}
return null;
}
public int [] getByDiameterMulti(
final boolean use_min_dia,
final boolean use_same_dia,
final int [] start_ij,
final int ok_dist,
final int debugLevel) {
final boolean [] tried = new boolean [pairs.length];
final Thread[] threads = ImageDtt.newThreadArray();
final AtomicInteger ai = new AtomicInteger(0);
final AtomicInteger abest_dia= new AtomicInteger(pair_state.length);
final AtomicBoolean ago = new AtomicBoolean(true);
final int [][] best_row_col= new int [pair_state.length+1][];
// final boolean use_min_dia = (heur & HEUR_MIN_DIA) != 0;
// final boolean use_same_dia = (heur & HEUR_SAME_DIA) != 0;
final int [] same_row_col = {-1,-1};
// find minimal diameter or first that does not increase it
for (int pass = 0; pass <2; pass++) {
if (pass == 0) {
for (int n = 0; n < pairs.length; n++) if ((pairs[n] != null) && (pairs[n][0]>=start_ij[0])){
if ((pairs[n][0] >start_ij[0]) || (pairs[n][1] > start_ij[1])) {
ai.set(n);
break;
}
}
} else {
ai.set(0); // start from the very beginning on pass 1
}
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() {
public void run() {
for (int n = ai.getAndIncrement(); n < pairs.length; n = ai.getAndIncrement()) if (ago.get() && (pairs[n] != null) && !tried[n]){
tried[n] = true; // for the second pass
int row = pairs[n][0];
int col = pairs[n][1];
if ((pair_state[row][col] == PAIR_UNDEFINED) && (groups[row] != groups[col])) {
int [] dias = newDiameterSingle(pairs[n]);
int d = dias[0];
if (use_min_dia && (d <= ok_dist)) {
ago.set(false); // got it, other threads may finish
}
int was_min = abest_dia.getAndAccumulate(dias[0], Math::min);
if (was_min > d) { // I set it
best_row_col[d] = pairs[n];
}
if (use_same_dia && (dias[0] <= dias[1])) {
boolean i_stopped = ago.getAndSet(false);
if (i_stopped) {
same_row_col[0] = pairs[n][0];
same_row_col[1] = pairs[n][1];
}
}
}
}
}
};
}
ImageDtt.startAndJoin(threads);
if (!ago.get()) { // was stopped after condition met (on any pass)
if (same_row_col[0] >= 0) {
return new int [] {same_row_col[0],same_row_col[1], HEUR_SAME_DIA};
} else {
int [] row_col = best_row_col[abest_dia.get()];
return new int [] {row_col[0],row_col[1], HEUR_MIN_DIA, ok_dist}; // keep same distance
}
}
} // for (int pass = 0; pass <2; pass++)
if (use_min_dia) {
int [] row_col = best_row_col[abest_dia.get()];
return new int [] {row_col[0],row_col[1], HEUR_MIN_DIA, abest_dia.get()}; // update (increase) best distance
}
return null;
}
public int [] getByDiameterSingle(
boolean use_min_dia,
boolean use_same_dia,
int [] start_ij,
int ok_dist,
int debugLevel) {
boolean [] tried = new boolean [pairs.length];
int [] best_row_col= null;
int [] same_row_col = null;
// find minimal diameter or first that does not increase it
int start_indx=0;
boolean ago= true;
int best_dia = pair_state.length;
for (int pass = 0; pass <2; pass++) {
if (pass == 0) {
for (int n = 0; n < pairs.length; n++) if ((pairs[n] != null) && (pairs[n][0]>=start_ij[0])){
if ((pairs[n][0] >start_ij[0]) || (pairs[n][1] > start_ij[1])) {
start_indx = n;
break;
}
}
} else {
start_indx = 0;
}
for (int n = start_indx; n < pairs.length; n++) if ((pairs[n] != null) && !tried[n]){
tried[n] = true; // for the second pass
int row = pairs[n][0];
int col = pairs[n][1];
if ((pair_state[row][col] == PAIR_UNDEFINED) && (groups[row] != groups[col])) {
int [] dias = newDiameterSingle(pairs[n]);
int d = dias[0];
if (d < best_dia) {
best_dia = d;
best_row_col = pairs[n];
}
if (use_min_dia && (d <= ok_dist)) {
ago = false; // got it, other threads may finish
break;
}
if (use_same_dia && (dias[0] <= dias[1])) {
same_row_col = pairs[n];
}
}
}
if (!ago) { // was stopped after condition met (on any pass)
if (same_row_col != null) {
return new int [] {same_row_col[0],same_row_col[1], HEUR_SAME_DIA};
} else {
return new int [] {best_row_col[0],best_row_col[1], HEUR_MIN_DIA, ok_dist}; // keep same distance
}
}
} // for (int pass = 0; pass <2; pass++)
if (use_min_dia) {
return new int [] {best_row_col[0],best_row_col[1], HEUR_MIN_DIA, best_dia}; // update (increase) best distance
}
return null;
}
public int [] newDiameterMulti( // or return a pair - new/old?
final int [] pair) { // symmetrical
final int i = pair[0], j = pair[1];
final Thread[] threads = ImageDtt.newThreadArray();
final AtomicInteger ai = new AtomicInteger(0);
final AtomicInteger amax_new = new AtomicInteger(0);
final AtomicInteger amax_old = new AtomicInteger(0);
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() {
public void run() {
for (int n = ai.getAndIncrement(); n < dist.length; n = ai.getAndIncrement()) if ((dist[i][n] > 0) || (n==i)){
int d1 = dist[i][n]+1;
amax_new.getAndAccumulate(d1, Math::max);
int mx = d1-1;
for (int m = 0; m < dist.length; m++) if (dist[j][m] > 0){
int d = d1+ dist[j][m];
amax_new.getAndAccumulate(d, Math::max);
mx = Math.max(mx, dist[j][m]);
}
amax_old.getAndAccumulate(mx, Math::max);
}
}
};
}
ImageDtt.startAndJoin(threads);
return new int [] {amax_new.get(),amax_old.get()};
}
public int [] newDiameterSingle( // or return a pair - new/old?
final int [] pair) {
final int i = pair[0], j = pair[1];
int max_new = 0, max_old = 0;
for (int n = 0; n < dist.length; n++) if ((dist[i][n] > 0) || (n==i)){
int d1 = dist[i][n]+1;
max_new = Math.max(max_new, d1);
max_old = Math.max(max_old, d1-1);
for (int m = 0; m < dist.length; m++) if (dist[j][m] > 0){
int d = d1+ dist[j][m];
max_new = Math.max(max_new, d);
max_old = Math.max(max_old, dist[j][m]);
}
}
return new int [] {max_new,max_old};
}
public int [][] getPairsIndicesSingle(){
int n = pair_state.length;
int n2 = n * n;
int [][] pairs0 = new int[n * (n-1) / 2][];
int indx = 0;
for (int i = 0; i < n2; i++){
int row = i / n;
int col = i % n;
if ((col > row ) && (groups[col] != groups[row]) && (pair_state[row][col] == PAIR_UNDEFINED)) {
pairs0[indx++] = new int[] {row,col};
}
}
final int [][] pairs = new int [indx][2];
for (int i = 0; i < pairs.length; i++){
pairs[i][0] = pairs0[i][0];
pairs[i][1] = pairs0[i][1];
}
return pairs;
}
public int [][] getPairsIndicesMulti(){
final int n = pair_state.length;
final int n2 = n * n;
final int [][] pairs0 = new int[n * (n-1) / 2][];
final Thread[] threads = ImageDtt.newThreadArray();
final AtomicInteger ai = new AtomicInteger(0);
final AtomicInteger aindx = new AtomicInteger(0);
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() {
public void run() {
for (int i = ai.getAndIncrement(); i < n2; i = ai.getAndIncrement()){
int row = i / n;
int col = i % n;
if ((col > row ) && (groups[col] != groups[row]) && (pair_state[row][col] == PAIR_UNDEFINED)) {
int indx = aindx.getAndIncrement();
pairs0[indx] = new int[] {row,col};
}
}
}
};
}
ImageDtt.startAndJoin(threads);
ai.set(0);
final int [][] pairs = new int [aindx.get()][2];
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() {
public void run() {
for (int i = ai.getAndIncrement(); i < pairs.length; i = ai.getAndIncrement()){
pairs[i][0] = pairs0[i][0];
pairs[i][1] = pairs0[i][1];
}
}
};
}
ImageDtt.startAndJoin(threads);
return pairs;
}
public void recordPair(
int [] pair,
boolean success,
boolean multi) { // multithreaded mode
int i = pair[0], j = pair[1];
if (success) {
pair_state[i][j] = PAIR_DEFINED;
// merge groups
if (groups[i] != groups[j]) {
int g0 = Math.min(groups[i], groups[j]);
int g1 = Math.max(groups[i], groups[j]);
for (int k = 0; k < groups.length; k++) {
if (groups[k] == g1) {
groups[k] = g0;
}
}
}
if (multi) {
updateDistanceMulti(pair);
} else {
updateDistanceSingle(pair);
}
} else {
pair_state[i][j] = PAIR_FAILED;
}
}
public void updateDistanceMulti(
final int [] pair) { // symmetrical
final int i = pair[0], j = pair[1];
final int [][] dist2 = {dist[i].clone(),dist[j].clone()};
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() {
// pair should be disconnected, so n from one thread != m from the other
for (int n = ai.getAndIncrement(); n < dist.length; n = ai.getAndIncrement()) if ((dist2[0][n] > 0) || (n == i)){
int d1 = dist2[0][n]+1;
for (int m = 0; m < dist.length; m++) if ((dist2[1][m] > 0) || ((m == j) && (n != i))){
int d = d1+ dist2[1][m];
dist[n][m] = d;
dist[m][n] = d;
}
}
}
};
}
ImageDtt.startAndJoin(threads);
dist[i][j] = 1;
dist[j][i] = 1;
return;
}
public void updateDistanceSingle(
final int [] pair) { // symmetrical
final int i = pair[0], j = pair[1];
final int [][] dist2 = {dist[i].clone(),dist[j].clone()};
for (int n = 0; n < dist.length; n++) if ((dist2[0][n] > 0) || (n == i)){
int d1 = dist2[0][n]+1;
for (int m = 0; m < dist.length; m++) if ((dist2[1][m] > 0) || ((m == j) && (n != i))){
int d = d1+ dist2[1][m];
dist[n][m] = d;
dist[m][n] = d;
}
}
dist[i][j] = 1;
dist[j][i] = 1;
return;
}
}
......@@ -9,7 +9,7 @@ import Jama.Matrix;
public class PairwiseOrthoMatch implements Serializable {
private static final long serialVersionUID = 1L;
public double [][] affine = new double[2][3];
private double [][] affine = new double[2][3];
public transient double [][] jtj = new double [6][6];
public int zoom_lev;
public double rms = Double.NaN;
......@@ -17,12 +17,14 @@ public class PairwiseOrthoMatch implements Serializable {
public transient double overlap = 0.0;
public transient double [] equalize1to0 = {1,0}; // value1 = equalize2to1[0]*value2+equalize2to1[1]
public PairwiseOrthoMatch() {
}
// public PairwiseOrthoMatch() {}
public double getOverlap() {
return overlap;
}
public void setOverlap(double overlap) {
this.overlap =overlap;
}
public PairwiseOrthoMatch(
double [][] affine,
double [][] jtj,
......@@ -36,6 +38,9 @@ public class PairwiseOrthoMatch implements Serializable {
this.overlap = overlap;
}
public boolean isDefined() {
return affine != null;
}
public double [] getEqualize2to1() {
return equalize1to0;
}
......@@ -52,7 +57,7 @@ public class PairwiseOrthoMatch implements Serializable {
}
public PairwiseOrthoMatch clone() {
double [][] affine = {this.affine[0].clone(),this.affine[1].clone()};
double [][] affine = (this.affine==null) ? null: (new double [][] {this.affine[0].clone(),this.affine[1].clone()});
double [][] jtj = new double [this.jtj.length][];
for (int i = 0; i < this.jtj.length; i++) {
jtj[i] = this.jtj[i].clone();
......@@ -221,9 +226,16 @@ public class PairwiseOrthoMatch implements Serializable {
}
public double [][] getAffine(){
if (affine == null) {
return new double [][] {{1,0,0},{0,1,0}};
}
return affine;
}
public void setAffine(double [][] affine) {
this.affine= affine;
}
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject();
for (int i = 0; i < jtj.length; i++) {
......
......@@ -609,6 +609,7 @@ public class ElphelTiffReader extends TiffReader{ // BaseTiffReader {
}
public static Properties getTiffMeta(String path) throws IOException {
// System.out.println("getTiffMeta(): "+path);
Properties properties = new Properties();
File file = new File(path);
String extension = getFileExtension(file);
......@@ -620,7 +621,7 @@ public class ElphelTiffReader extends TiffReader{ // BaseTiffReader {
ImageReader reader = readers.next();
stream = ImageIO.createImageInputStream(file);
reader.setInput(stream, true);
IIOMetadata metadata = reader.getImageMetadata(0);
IIOMetadata metadata = reader.getImageMetadata(0); //java.lang.IllegalStateException: Input not set!
TIFFDirectory ifd = TIFFDirectory.createFromMetadata(metadata);
TIFFField [] tfs = ifd.getTIFFFields();
for (TIFFField tf: tfs) {
......
......@@ -11639,6 +11639,7 @@ public class ImageDttCPU {
*/
public static void startAndJoin(Thread[] threads) // USED in lwir
{
// System.out.println("startAndJoin()");
for (int ithread = 0; ithread < threads.length; ++ithread)
{
threads[ithread].setPriority(Thread.NORM_PRIORITY);
......
......@@ -29,6 +29,7 @@ import java.util.Properties;
import java.util.StringTokenizer;
import com.elphel.imagej.common.GenericJTabbedDialog;
import com.elphel.imagej.orthomosaic.ComboMatch;
public class IntersceneMatchParameters {
public static String [] MODES3D = {"RAW", "INF", "FG", "BG"}; // RAW:-1
......@@ -94,6 +95,9 @@ public class IntersceneMatchParameters {
public boolean sfm_show_corr = false; // Show combined SfM for each refinement run
// rectilinear matching parameters
public String omtch_img_set = ComboMatch.FILES_LISTS_PATHS[0]; // image s
public int rln_gpu_width = 4096; // horizontal GPU image size (applied during init)
public int rln_gpu_height = 4096; // vertical GPU image size (applied during init)
......@@ -172,6 +176,8 @@ public class IntersceneMatchParameters {
public int pequ_num_iter = 100; // 50;
public int pequ_debugLevel = 2;
public String patt_save_top = "/media/elphel/NVME/lwir16-proc/ortho_videos/debug/sept12-13/pattern_match/";
public String patt_save_subdir = "combo_maps";
public double [] getImsMountATR() {
......@@ -737,11 +743,16 @@ public class IntersceneMatchParameters {
"Show combined SfM for each refinement run.");
gd.addTab ("Mosaic", "Combining scene series into a composite map map");
gd.addChoice ("Files list/data path (w/o extension):", ComboMatch.FILES_LISTS_PATHS, omtch_img_set);
gd.addNumericField("GPU window width", this.rln_gpu_width, 0,5,"pix",
"Horizontal GPU image size (applied during init.)");
gd.addNumericField("GPU window height", this.rln_gpu_height, 0,5,"pix",
"Vertical GPU image size (applied during init).");
gd.addMessage ("Rectilinear image matching");
gd.addNumericField("Phase correlation fat zero", this.rln_fat_zero, 5,8,"",
"Phase correlation fat zero - was set to 10000.");
......@@ -830,6 +841,10 @@ public class IntersceneMatchParameters {
gd.addNumericField("LMA iterations", this.pequ_num_iter, 0,3,"",".Maximal number of the LMA iterations.");
gd.addNumericField("Debug level for equalization",this.pequ_debugLevel, 0,3,"","Debug level for global (LMA-based) intensity equalization.");
gd.addMessage ("Pattern match parameters");
gd.addStringField ("Pattern match save directory",this.patt_save_top, 120, "Top directory to save combo maps");
gd.addStringField ("Save subdirectory", this.patt_save_subdir, 80, "Subdirectory for versions of the same scene/pair of scenes");
//
......@@ -1663,6 +1678,8 @@ public class IntersceneMatchParameters {
this.sfm_show_corr_ind = gd.getNextBoolean();
this.sfm_show_corr = gd.getNextBoolean();
this.omtch_img_set = ComboMatch.FILES_LISTS_PATHS[gd.getNextChoiceIndex()];
this.rln_gpu_width = (int) gd.getNextNumber();
this.rln_gpu_height = (int) gd.getNextNumber();
this.rln_fat_zero = gd.getNextNumber();
......@@ -1737,6 +1754,9 @@ public class IntersceneMatchParameters {
this.pequ_num_iter = (int) gd.getNextNumber();
this.pequ_debugLevel = (int) gd.getNextNumber();
this.patt_save_top= gd.getNextString();
this.patt_save_subdir = gd.getNextString();
this.center_reference = gd.getNextBoolean();
this.overlap_sequences = gd.getNextBoolean();
this.reset_photometric = gd.getNextBoolean();
......@@ -2185,6 +2205,7 @@ public class IntersceneMatchParameters {
properties.setProperty(prefix+"sfm_show_seq", this.sfm_show_seq + ""); // boolean
properties.setProperty(prefix+"sfm_show_corr_ind", this.sfm_show_corr_ind + ""); // boolean
properties.setProperty(prefix+"sfm_show_corr", this.sfm_show_corr + ""); // boolean
properties.setProperty(prefix+"omtch_img_set", this.omtch_img_set + ""); // String
properties.setProperty(prefix+"rln_gpu_width", this.rln_gpu_width+""); // int
properties.setProperty(prefix+"rln_gpu_height", this.rln_gpu_height+""); // int
......@@ -2261,6 +2282,9 @@ public class IntersceneMatchParameters {
properties.setProperty(prefix+"pequ_num_iter", this.pequ_num_iter + ""); // int
properties.setProperty(prefix+"pequ_debugLevel", this.pequ_debugLevel + ""); // int
properties.setProperty(prefix+"patt_save_top", this.patt_save_top + ""); // String
properties.setProperty(prefix+"patt_save_subdir", this.patt_save_subdir + ""); // String
properties.setProperty(prefix+"center_reference", this.center_reference + ""); // boolean
properties.setProperty(prefix+"overlap_sequences", this.overlap_sequences + ""); // boolean
properties.setProperty(prefix+"reset_photometric", this.reset_photometric + ""); // boolean
......@@ -2673,6 +2697,8 @@ public class IntersceneMatchParameters {
if (properties.getProperty(prefix+"sfm_show_corr_ind")!=null) this.sfm_show_corr_ind=Boolean.parseBoolean(properties.getProperty(prefix+"sfm_show_corr_ind"));
if (properties.getProperty(prefix+"sfm_show_corr")!=null) this.sfm_show_corr=Boolean.parseBoolean(properties.getProperty(prefix+"sfm_show_corr"));
if (properties.getProperty(prefix+"omtch_img_set")!=null) this.omtch_img_set=(String) properties.getProperty(prefix+ "omtch_img_set");
if (properties.getProperty(prefix+"rln_gpu_width")!=null) this.rln_gpu_width=Integer.parseInt(properties.getProperty(prefix+"rln_gpu_width"));
if (properties.getProperty(prefix+"rln_gpu_height")!=null) this.rln_gpu_height=Integer.parseInt(properties.getProperty(prefix+"rln_gpu_height"));
if (properties.getProperty(prefix+"rln_fat_zero")!=null) this.rln_fat_zero=Double.parseDouble(properties.getProperty(prefix+"rln_fat_zero"));
......@@ -2747,6 +2773,9 @@ public class IntersceneMatchParameters {
if (properties.getProperty(prefix+"pequ_num_iter")!=null) this.pequ_num_iter=Integer.parseInt(properties.getProperty(prefix+ "pequ_num_iter"));
if (properties.getProperty(prefix+"pequ_debugLevel")!=null) this.pequ_debugLevel=Integer.parseInt(properties.getProperty(prefix+ "pequ_debugLevel"));
if (properties.getProperty(prefix+"patt_save_top")!=null) this.patt_save_top=(String) properties.getProperty(prefix+ "patt_save_top");
if (properties.getProperty(prefix+"patt_save_subdir")!=null) this.patt_save_subdir=(String) properties.getProperty(prefix+ "patt_save_subdir");
if (properties.getProperty(prefix+"center_reference")!=null) this.center_reference=Boolean.parseBoolean(properties.getProperty(prefix+"center_reference"));
if (properties.getProperty(prefix+"overlap_sequences")!=null) this.overlap_sequences=Boolean.parseBoolean(properties.getProperty(prefix+"overlap_sequences"));
if (properties.getProperty(prefix+"reset_photometric")!=null) this.reset_photometric=Boolean.parseBoolean(properties.getProperty(prefix+"reset_photometric"));
......@@ -3185,6 +3214,7 @@ public class IntersceneMatchParameters {
imp.sfm_show_seq = this.sfm_show_seq;
imp.sfm_show_corr_ind = this.sfm_show_corr_ind;
imp.sfm_show_corr = this.sfm_show_corr;
imp.omtch_img_set = this.omtch_img_set;
imp.rln_gpu_width = this.rln_gpu_width;
imp.rln_gpu_height = this.rln_gpu_height;
......@@ -3260,6 +3290,9 @@ public class IntersceneMatchParameters {
imp.pequ_num_iter = this.pequ_num_iter;
imp.pequ_debugLevel = this.pequ_debugLevel;
imp.patt_save_top = this.patt_save_top;
imp.patt_save_subdir = this.patt_save_subdir;
imp.center_reference = this.center_reference;
imp.overlap_sequences = this.overlap_sequences;
imp.reset_photometric = this.reset_photometric;
......
......@@ -223,16 +223,6 @@ public class QuadCLTCPU {
}
@Deprecated
public double [][] getPimuOffsets() {
return pimu_offsets;
}
@Deprecated
public void setPimuOffsets(double[][] offsets) { // never
pimu_offsets = offsets;
}
// find best rotation between IMU XYZ and camera XYZ
/**
* Refine scene poses (now only linear) from currently adjusted poses
......
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