Commit 8667ae4b authored by Andrey Filippov's avatar Andrey Filippov

Object matching in different flights

parent 02845a72
......@@ -849,6 +849,7 @@ public class Eyesis_Correction implements PlugIn, ActionListener {
addButton("Read Tiff", panelLWIRWorld, color_process);
addButton("Test video", panelLWIRWorld, color_process);
addButton("Ortho Pairs", panelLWIRWorld, color_process);
addButton("Extract Objects", panelLWIRWorld, color_process);
addButton("Mismatched resolutions", panelLWIRWorld, color_process);
addButton("Generate DATI", panelLWIRWorld, color_process);
addButton("Create mine", panelLWIRWorld, color_process);
......@@ -5708,7 +5709,7 @@ public class Eyesis_Correction implements PlugIn, ActionListener {
e.printStackTrace();
}
// ComboMatch.testReadTiff();
} else if (label.equals("Ortho Pairs")) {
} else if (label.equals("Ortho Pairs") || label.equals("Extract Objects")) {
DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
EYESIS_CORRECTIONS.setDebug(DEBUG_LEVEL);
if (GPU_TILE_PROCESSOR == null) {
......@@ -5729,6 +5730,7 @@ public class Eyesis_Correction implements PlugIn, ActionListener {
ComboMatch.openTestPairGps(
CLT_PARAMETERS, // CLTParameters clt_parameters,
GPU_TILE_PROCESSOR,
label.equals("Extract Objects"), // boolean extract_mines,
DEBUG_LEVEL);
} else if (label.equals("Test video")) {
ImagePlus imp_sel = WindowManager.getCurrentImage();
......
package com.elphel.imagej.orthomosaic;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import ij.Prefs;
public class AltitudeMismatchKernel implements Serializable{
private static final long serialVersionUID = 1L;
public static String kernels_directory;
public int zoom_level;
public double agl_from;
public double agl_to;
public String filename;
public AltitudeMismatchKernel (
String filename,
int zoom_level,
double agl_from,
double agl_to) {
this.filename = filename;
this.zoom_level = zoom_level;
this.agl_from = agl_from;
this.agl_to = agl_to;
}
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject();
}
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
ois.defaultReadObject();
}
public static void setKernelsDirectory(
String directory) {
while (directory.endsWith(Prefs.getFileSeparator())){
directory=directory.substring(0, directory.length()-1);
}
kernels_directory = directory;
}
public static String getKernelsDirectory() {
return kernels_directory;
}
public String getKernelPath() {
return kernels_directory+Prefs.getFileSeparator()+filename;
}
public static AltitudeMismatchKernel getKernel(
int zoom_level,
double agl_from,
double agl_to,
ArrayList<AltitudeMismatchKernel> kernels) {
double tolerance = 0.15;
double agl_from_min = agl_from * (1.0 - tolerance);
double agl_from_max = agl_from * (1.0 + tolerance);
double agl_to_min = agl_to * (1.0 - tolerance);
double agl_to_max = agl_to * (1.0 + tolerance);
for (AltitudeMismatchKernel kernel:kernels) {
if (zoom_level == kernel.zoom_level) {
if ( (kernel.agl_from >= agl_from_min) &&
(kernel.agl_from <= agl_from_max) &&
(kernel.agl_to >= agl_to_min) &&
(kernel.agl_to <= agl_to_max))
return kernel;
}
}
return null;
}
}
......@@ -46,6 +46,7 @@ public class ComboMatch {
public static boolean openTestPairGps(
CLTParameters clt_parameters,
GPUTileProcessor gpu_tile_processor, // initialized by the caller
boolean extract_mines,
int debugLevel) {
GPU_TILE_PROCESSOR = gpu_tile_processor;
PairwiseOrthoMatch pairwiseOrthoMatch = null;
......@@ -85,11 +86,13 @@ public class ComboMatch {
// 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_nov3_50-75",
"/media/elphel/SSD3-4GB/lwir16-proc/ortho_videos/maps_sep12-50m"};
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";
// 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
//maps_19_sep13.list
//maps_09_short.list
......@@ -133,10 +136,16 @@ public class ComboMatch {
boolean process_correlation = true; // use false to save new version of data
boolean update_match = true; // use false to save new version of data
boolean render_match = true;
boolean pattern_match = false;
boolean bounds_to_indices = true;
int temp_mode = 1;
boolean restore_temp = true;
double frac_remove = 0.15;
double metric_error = 0.05; // 0.02;// 2 cm
boolean update_lla = false; // re-read file metadata
boolean update_kernel_patterns = false;
if (!use_marked_image) {
process_correlation=false; // use already adjusted by default
}
......@@ -149,6 +158,13 @@ public class ComboMatch {
gd.addCheckbox ("Process correlations", process_correlation, "false to skip to just regenerate new save file.");
gd.addCheckbox ("Update match if calculated", update_match, "Will update correlation match for a pair if found.");
gd.addCheckbox ("Render match", render_match, "Render a pair of matched images.");
gd.addCheckbox ("Pattern match", pattern_match, "Search for patterns for both images in a pair, first is primary.");
gd.addCheckbox ("Bounds to selected images", bounds_to_indices, "Set combo image bounds to selected images only. False - all images.");
gd.addNumericField("Temp mode", temp_mode, 0,4,"",
"O - do not modify average pixels, 1 - equalize second image to first, 2 - try to account for raw average");
//temp_mode
/*
for (int n = 0; n < image_enuatr.length; n++) {
gd.addMessage("image["+n+"] pose");
......@@ -174,18 +190,24 @@ public class ComboMatch {
if (use_marked_image ) {
gd.addCheckbox ("Use marked image data", true, "Use markes from the selected image");
}
gd.addCheckbox ("Update files metadata", update_lla, "Re-read files metadata (ifd it was modified)");
gd.addCheckbox ("Update files metadata", update_lla, "Re-read files metadata (if it was modified)");
gd.addCheckbox ("Update kernels/patterns", update_kernel_patterns, "Re-read kernels and patterns from *.list file");
// update_kernel_patterns
gd.showDialog();
if (gd.wasCanceled()) return false;
int choice_index = gd.getNextChoiceIndex();
files_list_path = files_lists_paths[choice_index]+".list";
orthoMapsCollection_path =files_lists_paths[choice_index]+".data";
String files_list_path = files_lists_paths[choice_index]+".list";
String orthoMapsCollection_path =files_lists_paths[choice_index]+".data";
use_saved_collection = gd.getNextBoolean();
save_collection = gd.getNextBoolean();
process_correlation= gd.getNextBoolean();
update_match= gd.getNextBoolean();
render_match= gd.getNextBoolean();
pattern_match= gd.getNextBoolean();
bounds_to_indices= gd.getNextBoolean();
temp_mode = (int) gd.getNextNumber();
/*
for (int n = 0; n < image_enuatr.length; n++) {
image_enuatr[n][0][0] = gd.getNextNumber();
......@@ -214,6 +236,7 @@ public class ComboMatch {
use_marked_image= gd.getNextBoolean();
}
update_lla= gd.getNextBoolean();
update_kernel_patterns= gd.getNextBoolean();
OrthoMapsCollection maps_collection=null;
if (use_saved_collection) {
try {
......@@ -222,6 +245,11 @@ public class ComboMatch {
// TODO Auto-generated catch block
e.printStackTrace();
}
//files_list_path
if (update_kernel_patterns) {
maps_collection.updateKernels(files_list_path);
maps_collection.updatePatterns(files_list_path);
}
} else {
maps_collection = new OrthoMapsCollection(files_list_path); // should have ".list" extension
}
......@@ -266,6 +294,7 @@ public class ComboMatch {
false, // boolean use_alt,
show_centers, // boolean show_centers,
zoom_lev, // int zoom_level,
temp_mode, // int temp_mode, // 0 - do nothing, 1 - equalize average,2 - try to correct
origin); // int [] origin){
imp_img.show();
}
......@@ -276,10 +305,11 @@ public class ComboMatch {
true, // boolean use_alt,
show_centers, // boolean show_centers,
zoom_lev, // int zoom_level,
0, // int temp_mode, // 0 - do nothing, 1 - equalize average,2 - try to correct
origin); // int [] origin){
imp_alt.show();
}
if (process_correlation || render_match ) {
if (process_correlation || render_match || pattern_match) {
// int [] gpu_pair;
if (gpu_spair == null) {
ArrayList<Point> pairs_list = new ArrayList<Point>();
......@@ -409,40 +439,26 @@ public class ComboMatch {
System.out.println();
}
}
if (render_match) {
ImagePlus imp_img_pair = maps_collection.renderMulti (
"multi_"+gpu_spair[0]+"-"+gpu_spair[1]+"-zoom_"+min_zoom_lev+"_"+zoom_lev, // String title,
false, // boolean use_alt,
if (pattern_match) {
ImagePlus imp_pat_match = maps_collection.patterMatchDualWrap (
gpu_pair, // int [] indices, // null or which indices to use (normally just 2 for pairwise comparison)
affines, // double [][][] affines, // null or [indices.length][2][3]
warp, // FineXYCorr warp,
show_centers, // boolean show_centers,
min_zoom_lev, // int zoom_level,
origin); // int [] origin){
imp_img_pair.show();
/*
int gpu_tilesX = gpu_width/GPUTileProcessor.DTT_SIZE;
Rectangle tile_woi = OrthoMapsCollection.scaleRectangle (woi, GPUTileProcessor.DTT_SIZE);
double [][] vector_field_interpolated = interpolateVectorField(
vector_field, // final double [][] vector_field, // sparse {vx,vy,strength}
gpu_tilesX, // final int gpu_tilesX, // 512
tile_woi, // final Rectangle tile_woi, // only width, height are used top-left corners are the same
scale, // final double scale,
1); // final int debugLevel);
warp); // FineXYCorr warp)
// imp_pat_match.show();
}
if (render_match) {
ImagePlus imp_img_pair = maps_collection.renderMulti (
"multi_"+gpu_spair[0]+"-"+gpu_spair[1]+"-zoom_"+min_zoom_lev+"_"+zoom_lev, // String title,
false, // boolean use_alt,
gpu_pair, // int [] indices, // null or which indices to use (normally just 2 for pairwise comparison)
bounds_to_indices, // boolean bounds_to_indices,
temp_mode, // int temp_mode, // 0 - do nothing, 1 - equalize average,2 - try to correct
affines, // double [][][] affines, // null or [indices.length][2][3]
warp, // FineXYCorr warp,
show_centers, // boolean show_centers,
min_zoom_lev, // int zoom_level,
origin); // int [] origin){
imp_img_pair.show();
*/
}
}
}
......
......@@ -5,6 +5,7 @@ import java.awt.Rectangle;
import com.elphel.imagej.gpu.GPUTileProcessor;
public class FineXYCorr {
public double scale_warp = 1.0;
public double [] top_left_metric; // relative to the reference image vertical point, normally negative
public int width_tiles;
public int height_tiles;
......@@ -60,7 +61,7 @@ public class FineXYCorr {
* Apply warping to the x,y pair
* @param xy {X,Y} before and after warping
*/
public void warpXY(// in render pixels
public void warpXY(// in render pixels NOT_USED
double [] xy) { // no interpolation - using nearest
int ix = (int) Math.round((xy[0] - render_tl[0]) * render_div_tile);
int iy = (int) Math.round((xy[1] - render_tl[1]) * render_div_tile);
......@@ -82,8 +83,8 @@ public class FineXYCorr {
double [] dxdy = warp_xy[ix + width_tiles * iy];
if (dxdy != null) {
return new double [] {
dxdy[0]*pix_div_render,
dxdy[1]*pix_div_render};
dxdy[0]*pix_div_render*scale_warp,
dxdy[1]*pix_div_render*scale_warp};
}
}
return new double [2];
......
package com.elphel.imagej.orthomosaic;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.ArrayList;
import ij.Prefs;
public class GroundObjectPattern implements Serializable {
private static final long serialVersionUID = 1L;
public static long UTC_EAST_HRS = 3;
public static String patterns_directory;
//public LocalDateTime plusHours(long hours)
public int zoom_level; // 1 for 5mm/pix
public String object_type; // like "TM62"
// public String object_version; // just change config
LocalDateTime utcDateTime; // UTC
public String filename; // should be square
public GroundObjectPattern(
String filename,
int zoom_level,
String object_type,
LocalDateTime utcDateTime) {
this.filename = filename;
this.zoom_level = zoom_level;
this.object_type = object_type;
this.utcDateTime = utcDateTime;
}
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject();
}
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
ois.defaultReadObject();
}
public static void setPatternsDirectory(
String directory) {
while (directory.endsWith(Prefs.getFileSeparator())){
directory=directory.substring(0, directory.length()-1);
}
patterns_directory = directory;
}
public String getPatternPath() {
return patterns_directory+Prefs.getFileSeparator()+filename;
}
public static String getPatternsDirectory() {
return patterns_directory;
}
public static GroundObjectPattern getPattern(
String object_type,
LocalDateTime utcDateTime,
ArrayList<GroundObjectPattern> patterns) {
GroundObjectPattern best_gop = null;
long best_hrs_diff = 24;
for (GroundObjectPattern gop:patterns) {
if ((object_type == null) || object_type.equals(gop.object_type)) {
if (utcDateTime != null) {
long hrs_diff = Math.abs(utcDateTime.getHour()-gop.utcDateTime.getHour());
if (hrs_diff > 12) {
hrs_diff = 24 -hrs_diff;
}
if (hrs_diff < best_hrs_diff) {
best_gop = gop;
best_hrs_diff = hrs_diff;
}
} else {
best_gop = gop; // use latest
}
}
}
return best_gop;
}
public LocalDateTime getUTC() {
return utcDateTime;
}
public LocalDateTime getLocalDT() {
return utcDateTime.plusHours(UTC_EAST_HRS);
}
public boolean isEvening() {
return getLocalDT().getHour() >= 12;
}
public double getAGL() {
double agl = 50; // zoom 0 for 50m AGL
for (int z = zoom_level; z > 0; z--) {
agl /=2;
}
for (int z = zoom_level; z < 0; z++) {
agl *=2;
}
return agl;
}
public int getZoomLevel() {
return zoom_level;
}
}
package com.elphel.imagej.orthomosaic;
public class ObjectLocation {
String name;
double [] xy_meters;
}
......@@ -57,8 +57,9 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{
public static boolean FIX_VERT_Y = false; // true; // temporarily fix vertical Y coordinate bug (use -GCORR in the filename?)
public static int gpu_width = 4096;
public static int gpu_height = 4096;
public static final String [] KEY_DIRS= {"rootDirectory", // from EyesisCorrectionParameters
"sourceDirectory","linkedModels","videoDirectory","x3dDirectory","resultsDirectory"};
// public static final String [] KEY_DIRS= {"rootDirectory", // from EyesisCorrectionParameters
// "sourceDirectory","linkedModels","videoDirectory","x3dDirectory","resultsDirectory",
// "kernelsDirectory", "patternsDirectory"};
public static final String [] kernel_paths = {
"--- no convolution (same altitude) ---",
"/media/elphel/NVME/lwir16-proc/ortho_videos/kernel_25_50.tiff",
......@@ -94,7 +95,7 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{
transient HashMap <Integer, FloatImageData> images;
HashMap <String, PairwiseOrthoMatch> pairwise_matches;
public transient SensorTemperatureData[] temp_data;
public transient double agl = Double.NaN;
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject();
oos.writeObject(path);
......@@ -114,6 +115,7 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{
// images is not saved
// pairwise_matches is not transient
oos.writeObject(temp_data); // temporary, while transient
oos.writeObject(agl);
}
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
......@@ -132,6 +134,9 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{
// orig_image was not saved
// alt_image was not saved
temp_data = (SensorTemperatureData[]) ois.readObject();
agl = (double) ois.readObject();
images = new HashMap <Integer, FloatImageData>(); // field images was not saved
averageImagePixel = Double.NaN; // average image pixel value (to combine with raw)
......@@ -482,6 +487,26 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{
return true;
}
/**
* Get average altitude above ground (AGL)
* @return AGL or NaN
*/
public double getAGL() {
if (Double.isNaN(agl)) {
final FloatImageData src_elev = getAltData();
if (src_elev == null) {
System.out.println("getTileElevations(): No elevation data available for "+name);
return Double.NaN;
}
double average_asl = getMaskedAverage (
src_elev.data, // final double [] data,
null); // final boolean [] mask)
agl= lla[2] - average_asl;
}
return agl;
}
public ImagePlus getOriginalImage(boolean show_markers) {
FloatImageData orig_image = getImageData();
if (orig_image != null) {
......@@ -764,10 +789,6 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{
final double [] elev = new double [tilesX*tilesY];
Arrays.fill(elev, Double.NaN);
final int diff_zoom_lev = zoom_level-orig_zoom_level;
// double s = 1.0;
// int zl = diff_zoom_lev;
// for (; zl< 0; zl++) s *= 2.0;
// for (; zl> 0; zl--) s *= 0.5; // normally will not happen
final double scale = 1.0/getScale (diff_zoom_lev); // int zoom_lev);
final Thread[] threads = ImageDtt.newThreadArray();
......@@ -1070,7 +1091,7 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{
}
/**
* Read one of the convolution kernels (or nun if no convolution is needed)
* Read one of the convolution kernels (or null if no convolution is needed)
* @return square kernel as a 1-d double array or null
*/
public static double [] getConvolutionKernel() {
......@@ -2500,6 +2521,42 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{
return avg/num;
}
/**
* Calculate average of a float array, ignore masked out (!mask[i] and NaNs in the data)
* @param data double data array
* @param mask optional (may be null) boolean mask - ignore data items that have corresponding mask element false
* @return average value of the non-NaN and not disabled by mask elements of data[]
*/
private static double getMaskedAverage (
final float [] data,
final boolean [] mask) {
final Thread[] threads = ImageDtt.newThreadArray();
final AtomicInteger ai = new AtomicInteger(0);
final AtomicInteger ati = new AtomicInteger(0);
final double [] avg_arr = new double [threads.length];
final double [] npix_arr = new double [threads.length];
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() {
public void run() {
int thread_num = ati.getAndIncrement();
for (int iPix = ai.getAndIncrement(); iPix < data.length; iPix = ai.getAndIncrement()) if (!Double.isNaN(data[iPix]) && ((mask == null) || (mask[iPix]))){
avg_arr[thread_num] += data[iPix];
npix_arr[thread_num] += 1.0;
}
}
};
}
ImageDtt.startAndJoin(threads);
double avg=0, num=0;
for (int i = 0; i < avg_arr.length; i++) {
avg+=avg_arr[i];
num+=npix_arr[i];
}
return avg/num;
}
/**
* Remove double [] data elements that are lower than threshold_low or greater than threshold_high, ignore masked out and NaN elements
* @param data double [] input data
......@@ -3136,37 +3193,77 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{
public static void testPatternCorrelate(
ImagePlus imp_src) {
String pattern_dir= "/media/elphel/SSD3-4GB/lwir16-proc/ortho_videos/debug/mines/pattern_25m_zoom1/synthetic/";
String pattern_file= "patterns_50m_zoom1_200x200.tiff";
int zoomout = 1;
int corr_size = 256;
// String pattern_file= "patterns_50m_zoom1_200x200.tiff";
String [] pattern_files=
{"patterns_50m_zoom1_200x200.tiff",
"patterns_50m_evening_zoom1_200x200.tiff",
"patterns_50m_evening_01_zoom1_200x200_00.tiff",
"patterns_50m_evening_02_zoom1_200x200_00.tiff",
"patterns_50m_evening_03_zoom1_200x200_00.tiff",
"patterns_50m_evening_04_zoom1_200x200_00.tiff",
"patterns_50m_evening_05_zoom1_200x200_00.tiff"};
int zoomout = 2; // 1;
int corr_size = 128; // 256;
double phaseCoeff = 0.5;
int debugLevel = 1;
double min_corr = 0.0025; // real max >0.005 with scale -500000;
double min_corr = 0.01; // 0.0025; // real max >0.005 with scale -500000;
double min_corr_full_rel = 0.75; // minimal correlation with full pattern
double full_preference = 1.4; // prefer full pattern if its strength is slightly lower than for partial
double max_min_ratio = 3.0; // ratio of halves best/worst to use half-pattern
boolean combine_full = true;
double adv_radius = 60.0; // pixels
int corr_radius = 30; // may use same as adv_radius; but not more
int corr_radius = (int) (Math.sqrt(0.5)* adv_radius) -1 ; // 30; // may use same as adv_radius; but not more
double [][] src_marks = null;
PointRoi pRoi = (PointRoi) imp_src.getRoi();
if (pRoi != null) {
FloatPolygon fp = pRoi.getContainedFloatPoints();
src_marks = new double[fp.npoints][2];
for (int i = 0; i < src_marks.length; i++) {
src_marks[i][0] = fp.xpoints[i];
src_marks[i][1] = fp.ypoints[i];
}
}
GenericJTabbedDialog gd = new GenericJTabbedDialog("Correlate image with pattern",1200,500);
gd.addStringField ("Pattern directory", pattern_dir, 180, "Absolute path including trailing \"/\".");
gd.addStringField ("Pattern filename", pattern_file,180, "Pattern filename.");
// gd.addStringField ("Pattern filename", pattern_file,180, "Pattern filename.");
gd.addChoice("Pattern filename:", pattern_files, pattern_files[pattern_files.length-1]);
gd.addNumericField("Zoom-out factor", zoomout, 0,4,"x", "Reduce pattern resolution to match image.");
gd.addNumericField("Phase correlation coefficient", phaseCoeff, 3,7,"","1.0 - pure phase correlation, 0.0 - regular correlation.");
gd.addNumericField("Minimal correlation", min_corr, 3,7,"","Minimal correlation value to keep.");
gd.addNumericField("Minimal correlation", min_corr, 5,7,"","Minimal correlation value to keep.");
gd.addNumericField("Minimal relative full correlation", min_corr_full_rel, 5,7,"","Minimal relative correlation value with full circular pattern to keep.");
gd.addNumericField("Prefer full pattern", full_preference, 3,7,"","Prefer full pattern to partial; scale by this value.");
gd.addNumericField("Max/min halves ratio", max_min_ratio, 3,7,"","Ratio of halves best/worst to use half-pattern.");
gd.addCheckbox ("Combine with full pattern", combine_full, "Multiply by normalized full pattern correlation maximum.");
gd.addNumericField("Adversarial distance", adv_radius, 1,6,"pix", "Suppress weaker if they have closer strong ones.");
gd.addNumericField("Correlation peak max radius", corr_radius, 0,4,"pix", "Limit correlation peak radius. Should be <= adversarial");
gd.addNumericField("Correlation peak max radius",corr_radius, 0,4,"pix", "Limit correlation peak radius. Should be <= adversarial");
gd.addNumericField("Correlation size", corr_size, 0,4,"pix", "Should be power of 2.");
gd.addNumericField("Debug level", debugLevel, 0,4,"", "Debug level.");
gd.showDialog();
if (gd.wasCanceled()) return;
pattern_dir= gd.getNextString();
pattern_file= gd.getNextString();
// pattern_file= gd.getNextString();
String pattern_file = pattern_files[gd.getNextChoiceIndex()];
zoomout= (int) gd.getNextNumber();
phaseCoeff= gd.getNextNumber();
min_corr= gd.getNextNumber();
min_corr_full_rel= gd.getNextNumber();
full_preference= gd.getNextNumber();
max_min_ratio= gd.getNextNumber();
combine_full = gd.getNextBoolean();
adv_radius= gd.getNextNumber();
corr_radius= (int) gd.getNextNumber();
corr_size= (int) gd.getNextNumber();
debugLevel= (int) gd.getNextNumber();
if (corr_radius > ( (int) (Math.sqrt(0.5)* adv_radius) -1)){
corr_radius = (int) (Math.sqrt(0.5)* adv_radius) -1;
System.out.println("testPatternCorrelate(): Reducing corr_radius to "+corr_radius+" to avoid thread races");
}
double min_corr_full = min_corr*min_corr_full_rel;
float [] fpixels = (float[]) imp_src.getProcessor().getPixels();
int width = imp_src.getWidth();
int height = imp_src.getHeight();
......@@ -3200,7 +3297,7 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{
double [][] corrs_out = new double[patterns.length][];
double [][] convolve_out = new double[patterns.length][];
double [][] lim_corr = new double[patterns.length][];
// double [][] lim_corr = new double[patterns.length][];
double [][] corr_patterns = new double[patterns.length][];
for (int n = 0; n < patterns.length; n++) {
corr_patterns[n] = patternZoomCropPad(
......@@ -3224,47 +3321,83 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{
phaseCoeff, // final double phaseCoeff,
debugLevel); // final int debugLevel) {
}
ShowDoubleFloatArrays.showArrays(
String [] patt_titles = new String[corrs_out.length];
for (int i = 0; i < patt_titles.length; i++) {
patt_titles[i] = "patt_"+i;
}
ImagePlus imp_corrs = ShowDoubleFloatArrays.makeArrays(
corrs_out,
width,
height,
true,
removeKnownExtension(imp_src.getTitle())+"-PATTERN_CORR-0");
removeKnownExtension(imp_src.getTitle())+"-PATTERN_CORRS",
patt_titles); // test_titles,
if (src_marks != null) {
PointRoi roi = new PointRoi();
roi.setOptions("label");
for (int i = 0; i < src_marks.length; i++) {
roi.addPoint(src_marks[i][0],src_marks[i][1]); // ,1);
}
imp_corrs.setRoi(roi);
}
imp_corrs.show();
//data
double [] bestcorr = new double [data.length];
double [] fullcorr = new double [data.length];
int [] bestpatt = new int [bestcorr.length];
ArrayList<Point> object_list = combineDirCorrs (
corrs_out, // final double [][] corrs,
width, // final int width,
bestcorr, // final double [] bestcorr
fullcorr, // final double [] fullcorr_in,
bestcorr, // final double [] bestcorr_in
bestpatt, // final int [] bestpatt_in,
min_corr, // final double min_corr,
min_corr_full, // final double min_corr_full,
full_preference, // final double full_preference,
max_min_ratio, // final double max_min_ratio,
combine_full, // final boolean combine_full, // multiply by normalized full pattern correlation maximum
adv_radius, // final double adv_radius,
corr_radius); // final int corr_radius)
System.out.println("testPatternCorrelate(): Found "+object_list.size()+" candidates");
ShowDoubleFloatArrays.showArrays(
ImagePlus imp_best = ShowDoubleFloatArrays.makeArrays(
bestcorr,
width,
height,
removeKnownExtension(imp_src.getTitle())+"-BEST_CORR");
double [] dbg_bestpatt = new double [bestpatt.length];
Arrays.fill(dbg_bestpatt, Double.NaN);
for (int i = 0; i <bestpatt.length; i++) {
if (bestpatt[i] >=0) {
dbg_bestpatt[i] = bestpatt[i];
}
}
ImagePlus imp_bestpatt = ShowDoubleFloatArrays.makeArrays(
dbg_bestpatt,
width,
height,
removeKnownExtension(imp_src.getTitle())+"-BEST_PATT");
if (src_marks != null) {
PointRoi roi = new PointRoi();
roi.setOptions("label");
for (int i = 0; i < src_marks.length; i++) {
roi.addPoint(src_marks[i][0],src_marks[i][1]); // ,1);
}
imp_best.setRoi(roi);
imp_bestpatt.setRoi(roi);
}
imp_best.show();
imp_bestpatt.show();
double [][] filtered_corrs = splitPattenCorrelations(
patterns.length, // final int num_patterns,
bestcorr, // final double [] bestcorr,
bestpatt); // final int [] bestpatt)
/*
for (int n = 0; n < patterns.length; n++) {
lim_corr[n] = corrs_out[n].clone();
for (int i = 0; i < lim_corr.length; i++) {
if (lim_corr[n][i] < min_corr) {
lim_corr[n][i] = 0;
}
}
}
*/
for (int n = 0; n < patterns.length; n++) {
convolve_out[n]= correlateWithPattern(
filtered_corrs[n],// corrs_out[n], // final double [] data,
......@@ -3278,7 +3411,6 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{
double [] convolved_merged = mergePattenConvolutions(
convolve_out); // final double [][] convs)
String [] rslt_titles =new String [patterns.length+2];
double [][] rslt = new double [patterns.length + 2][]; // ]{data, corr_out};
double [][] conv_rslt = new double [patterns.length + 2][]; // ]{data, corr_out};
......@@ -3295,20 +3427,39 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{
conv_rslt[i+2] = convolve_out[i];
rslt_titles[i+2] = "corr_"+pattern_labels[i];
}
ShowDoubleFloatArrays.showArrays(
ImagePlus imp_patt_corrs = ShowDoubleFloatArrays.makeArrays(
rslt,
width,
height,
true,
removeKnownExtension(imp_src.getTitle())+"-PATTERN_CORR",
rslt_titles);
ShowDoubleFloatArrays.showArrays(
if (src_marks != null) {
PointRoi roi = new PointRoi();
roi.setOptions("label");
for (int i = 0; i < src_marks.length; i++) {
roi.addPoint(src_marks[i][0],src_marks[i][1]); // ,1);
}
imp_patt_corrs.setRoi(roi);
}
imp_patt_corrs.show();
ImagePlus imp_patt_conv = ShowDoubleFloatArrays.makeArrays(
conv_rslt,
width,
height,
true,
removeKnownExtension(imp_src.getTitle())+"-PATTERN_CORR_CONVOLVE_min_corr"+min_corr,
rslt_titles);
if (src_marks != null) {
PointRoi roi = new PointRoi();
roi.setOptions("label");
for (int i = 0; i < src_marks.length; i++) {
roi.addPoint(src_marks[i][0],src_marks[i][1]); // ,1);
}
imp_patt_conv.setRoi(roi);
}
imp_patt_conv.show();
System.out.println("testPatternCorrelate(): correlation done");
}
......@@ -3320,7 +3471,7 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{
* @param width width of the correlation array
* @param bestcorr_in null or double array [corrs[0].length] will contain
* best correlation value from multiple patterns of corrs[][].
* @param bestbatt_in null or integer array [corrs[0].length] will contain
* @param bestpatt_in null or integer array [corrs[0].length] will contain
* number of the best pattern
* @param min_corr - minimal correlation value to use (lower - replace with 0)
* @param adv_radius - suppress weaker local maximums near the stronger one
......@@ -3336,7 +3487,6 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{
final double min_corr,
final double adv_radius,
final int corr_radius) {
// final int num_patt = corrs.length;
final int height = corrs[0].length/width;
final double [] bestcorr = (bestcorr_in != null) ? bestcorr_in: (new double [corrs[0].length]);
final int [] bestpatt = (bestpatt_in != null) ? bestpatt_in: (new int [corrs[0].length]);
......@@ -3345,7 +3495,6 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{
final int [] bestpatt0 = new int [corrs[0].length];
Arrays.fill(bestpatt, -1);
Arrays.fill(bestpatt0, -1); // maybe not needed
// final boolean [] local_max = new boolean [bestcorr.length];
final Thread[] threads = ImageDtt.newThreadArray();
final AtomicInteger ai = new AtomicInteger(0);
final TileNeibs tn = new TileNeibs(width, height);
......@@ -3390,7 +3539,7 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{
System.out.println("combineDirCorrs(): ipix="+ipix);
}
is_max: {
for (int dir = 0; dir > TileNeibs.DIRS; dir++) {
for (int dir = 0; dir < TileNeibs.DIRS; dir++) {
int ipix1 = tn.getNeibIndex(ipix, dir);
if ((ipix1 >= 0) && (bestcorr0[ipix1] > bestcorr0[ipix])){
break is_max;
......@@ -3468,6 +3617,217 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{
}
return list;
}
/**
* Combine results of the phase correlations with different patters
* (now with semi-obscured half-circles), using the best (highest value match),
* filter by correlation value and suppress weaker maximums by stronger neighbors
* @param corrs array of 2D correlation results [num_pattern][pixel]
* @param width width of the correlation array
* @param fullcorr_in null or double array [corrs[0].length] will contain
* full pattern (corrs[0][]) correlation value.
* @param bestcorr_in null or double array [corrs[0].length] will contain
* best correlation value from multiple patterns of corrs[][].
* @param bestpatt_in null or integer array [corrs[0].length] will contain
* number of the best pattern.
* @param min_corr minimal correlation value to use (lower - replace with 0)
* @param min_corr_full correlation with full pattern still needs to exceed this
* even if correlation with partial (obscured) pattern will
* be used
* @param full_preference when looking for the best pattern to match, increase correlation
* with full pattern
* @param max_min_ratio ratio of halves best/worst to use half-pattern
* @param combine_full multiply best pattern correlation by relative (to the center pixel)
* correlation by the full pattern correlation
* @param adv_radius suppress weaker local maximums near the stronger one
* @param corr_radius maximal correlation peak radius (zero out outside).
* Should be <= adv_radius to avoid thread races
* @return ArrayList of integer pairs - pixel index (x+y*width) and the pattern index
*/
public static ArrayList<Point> combineDirCorrs (
final double [][] corrs,
final int width,
final double [] fullcorr_in,
final double [] bestcorr_in,
final int [] bestpatt_in,
final double min_corr,
final double min_corr_full,
final double full_preference,
final double max_min_ratio, // ratio of halves best/worst to use half-pattern
final boolean combine_full, // multiply by normalized full pattern correlation maximum
final double adv_radius,
final int corr_radius) {
final int height = corrs[0].length/width;
final int full_patt_indx = 0;
final double [] fullcorr = (fullcorr_in != null) ? fullcorr_in: (new double [corrs[0].length]);
// before adversarial filtering
final Thread[] threads = ImageDtt.newThreadArray();
final AtomicInteger ai = new AtomicInteger(0);
final TileNeibs tn = new TileNeibs(width, height);
final AtomicInteger ati = new AtomicInteger(0);
ArrayList<ArrayList<Point>> lists = new ArrayList<ArrayList<Point>>();
for (int i = 0; i < threads.length; i++) {
lists.add(new ArrayList<Point>());
}
// create fullcorr and list using only full correlation. Later will deal with obscured patterns
final int iradius = (int) Math.ceil(adv_radius);
final double adv_radius2 = adv_radius * adv_radius;
final double corr_radius2 = corr_radius*corr_radius;
final double [] bestcorr = (bestcorr_in != null) ? bestcorr_in: (new double [corrs[0].length]);
final int [] bestpatt = (bestpatt_in != null) ? bestpatt_in: (new int [corrs[0].length]);
Arrays.fill(bestpatt, -1);
final int dbg_pix = -2827823; // 3649679;// #3
final int dbg_pix1 = -3649679;// #3
final int dbg_pix0= 1439210; // -2827822; // 3649679;// #3
final int dbg_pix2 = 1442449; // -2831060;
final int dbg_pix3 = 1439211; // 2827822;
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() {
public void run() {
int thread_num = ati.getAndIncrement();
final TileNeibs tn = new TileNeibs(width, height);
int corr_diameter = 2*corr_radius+1;
TileNeibs tnl = new TileNeibs(corr_diameter,corr_diameter);
int local_center = (2 * corr_radius + 2) * corr_radius;
boolean [] mask = new boolean [corr_diameter*corr_diameter];
for (int ipix = ai.getAndIncrement(); ipix < fullcorr.length; ipix = ai.getAndIncrement()) if (corrs[full_patt_indx][ipix] >= min_corr_full) {
boolean dbg= (ipix==dbg_pix) || (ipix==dbg_pix1) || (ipix==dbg_pix2) || (ipix==dbg_pix3);
if (dbg) {
System.out.println("combineDirCorrs(): ipix="+ipix);
}
is_max: {
for (int dir = 0; dir < TileNeibs.DIRS; dir++) {
int ipix1 = tn.getNeibIndex(ipix, dir);
if ((ipix1 >= 0) && (corrs[full_patt_indx][ipix1] > corrs[full_patt_indx][ipix])){
break is_max;
}
}
// compare with others in adversarial radius, if equal - use higher index ipix
for (int dy = -iradius; dy <= iradius; dy++) {
for (int dx = -iradius; dx <= iradius; dx++) {
double r2 = dx*dx + dy*dy;
if ((r2 < adv_radius2) && (r2 > 2.1)) {
int ipix1 = tn.getNeibIndex(ipix, dx, dy);
if (ipix1 >= 0) {
if (corrs[full_patt_indx][ipix1] > corrs[full_patt_indx][ipix]) {
break is_max;
} else if ((corrs[full_patt_indx][ipix1] == corrs[full_patt_indx][ipix]) && (ipix1 < ipix)) {
break is_max;
}
}
}
}
}
//corrs[full_patt_indx]
// find best patter here, using full_preference;
//max_min_ratio, // ratio of halves best/worst to use half-pattern
int patt = full_patt_indx;
double best_val = full_preference*corrs[full_patt_indx][ipix];
double max_val = corrs[1][ipix];
double min_val = max_val;
int max_indx = 1;
for (int i = 1; i < corrs.length; i++) {
if (corrs[i][ipix] > best_val) {
best_val = corrs[i][ipix];
patt = i;
}
if (corrs[i][ipix] > max_val) {
max_val = corrs[i][ipix];
max_indx = i;
}
if (corrs[i][ipix] < min_val) {
min_val = corrs[i][ipix];
}
}
if ((min_val == 0) ||(max_val/min_val > max_min_ratio)) {
patt = max_indx;
}
if (corrs[patt][ipix] < min_corr) {
break is_max; // too weak maximum
}
// int patt = bestpatt0[ipix];
lists.get(thread_num).add(new Point(ipix,patt));
// Below should be no conflicts as no intersect
Arrays.fill(mask, false);
for (int dy = -corr_radius; dy <= corr_radius; dy++) {
for (int dx = -corr_radius; dx <= corr_radius; dx++) {
double r2 = dx*dx + dy*dy;
if (r2 < corr_radius2) {
int ipix1 = tn.getNeibIndex(ipix, dx, dy);
// TODO: mark mask with continuous pixels
if (ipix1 >= 0) {
int li = tnl.getNeibIndex(local_center, dx, dy); // should be always
if (li < 0) {
System.out.println("combineDirCorrs(): BUG1 li<0");
} else {
mask[li] = (corrs[patt][ipix1] >= min_corr) && // only consider the same pattern
(corrs[full_patt_indx][ipix1] >= min_corr_full);
}
}
}
}
}
int [] clusters = tnl.enumerateClusters(
mask, // boolean [] tiles,
null, // int [] num_clusters,
false); // boolean ordered)
if (dbg) {
String[] dbg_titles={"mask","cluster"};
double [][] dbg_img = new double [2][mask.length];
for (int i = 0; i < mask.length; i++) {
dbg_img[0][i] = mask[i]? corrs.length:0;
dbg_img[1][i] = clusters[i];
}
ShowDoubleFloatArrays.showArrays(
dbg_img,
corr_diameter,
corr_diameter,
true,
"clusters_"+ipix,
dbg_titles);
}
int center_cluster = clusters[local_center];
// Copy continuous pixels from the selected pattern
for (int dy = -corr_radius; dy <= corr_radius; dy++) {
for (int dx = -corr_radius; dx <= corr_radius; dx++) {
int li = tnl.getNeibIndex(local_center, dx, dy); // should be always
if (li < 0) {
System.out.println("combineDirCorrs(): BUG2 li<0");
} else {
if (clusters[li] == center_cluster) {
int ipix1 = tn.getNeibIndex(ipix, dx, dy);
if (ipix1 == dbg_pix0) {
System.out.println("ipix1="+ipix1);
}
if (ipix1 >= 0) {
bestcorr[ipix1] = corrs[patt][ipix1];
if (combine_full) {
bestcorr[ipix1] *= corrs[full_patt_indx][ipix1]/corrs[full_patt_indx][ipix];
}
bestpatt[ipix1] = patt;
}
}
}
}
}
}
}
}
};
}
ImageDtt.startAndJoin(threads);
// combine all lists
ArrayList<Point> list = new ArrayList<Point>();
for (int i = 0; i < lists.size(); i++) {
list.addAll(lists.get(i));
}
return list;
}
/**
* Split correlation filter results into per-pattern ones before convolution.
* Temporary solution as convolution is needed only for a few found maximums
......@@ -3521,28 +3881,88 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{
public static void testPatternGenerate() {
int half_size = 100;
boolean half_pix = false; // center between pixels
boolean evening_mode = false; // glare around object, use radius
double radius_out= 42;
double edge_out = 6;
double scale_out = 1.5;
double radius = 30; // 32; // 32;
double edge= 15; // 4;
double radius_in= 15; // if 0 - skip
double edge_in = 8;
double scale_in = -0.05;
double scale = -200; // black 200
if (evening_mode) {
radius_out= 42;
edge_out = 8;
scale_out =-1.5; // relative to scale
radius = 30; // 32; // 32;
edge= 15; // 4;
radius_in= 10; // if 0 - skip
edge_in = 8;
scale_in = -0.7;
scale = -40; // black 200
} else {
radius_out= 42;
edge_out = 6;
scale_out =.08;
radius = 30; // 32; // 32;
edge= 15; // 4;
radius_in= 15; // if 0 - skip
edge_in = 8;
scale_in = -0.05;
scale = -170; // black 200
}
boolean normalize = false;
int halves_number = 8; // hidden mines - number of diameter cuts
double cut_frac = 0.6; // half-cut width fraction of diameter
int corr_size = 128;
int zoomout = 2;
boolean out_normalize = true;
GenericJTabbedDialog gd = new GenericJTabbedDialog("Create circulat pattern",1200,600);
boolean out_normalize = false; // true;
GenericJTabbedDialog gd0 = new GenericJTabbedDialog("Create circular pattern",1200,600);
gd0.addCheckbox ("Evening mode", evening_mode, "True - evening (cooling down), false - morning (warming up).");
gd0.showDialog();
if (gd0.wasCanceled()) return;
evening_mode= gd0.getNextBoolean();
if (evening_mode) {
radius_out= 62; // 42;
edge_out = 40; // 8;
scale_out =-1.5; // relative to scale
radius = 30; // 32; // 32;
edge= 24; // 8; // 4;
radius_in= 10; // if 0 - skip
edge_in = 8;
scale_in = -0.1; // 7;
scale = -40; // black 200
} else {
radius_out= 42;
edge_out = 6;
scale_out =.08;
radius = 30; // 32; // 32;
edge= 10; // 4;
radius_in= 15; // if 0 - skip
edge_in = 8;
scale_in = -0.05;
scale = -170; // black 200
}
GenericJTabbedDialog gd = new GenericJTabbedDialog("Create circular pattern",1200,600);
gd.addNumericField("Half pattern size", half_size, 0,4,"pix", "Pattern will be square, twice this side.");
gd.addCheckbox ("Pattern center between pixels", half_pix, "If false - center will be at integer pixel X/Y.");
// gd.addCheckbox ("Evening mode", evening_mode, "True - evening (cooling down), false - morning (warming up).");
gd.addNumericField("Radius at half height", radius, 3,7,"pix", ".");
gd.addNumericField("Edge width", edge, 3,7,"pix", "Transition from full on to zero.");
gd.addNumericField("Optional inner feature radius", radius_in, 3,7,"pix", "Put 0 if not needed.");
gd.addNumericField("Inner feature edge", edge_in, 3,7,"pix", "Transition for the inner feature.");
gd.addNumericField("Inner feature scale", scale_in, 3,7,"", "Negative OK.");
gd.addNumericField("Inner feature scale", scale_in, 3,7,"", "Relative to the main feature.");
gd.addNumericField("Optional outer feature radius", radius_out, 3,7,"pix", "Put 0 if not needed.");
gd.addNumericField("Outer feature edge", edge_out, 3,7,"pix","Transition for the outer feature.");
gd.addNumericField("Outer feature scale", scale_out, 3,7,"", "Relative to the main feature.");
gd.addNumericField("Overall pattern scale", scale, 3,7,"", "Multiply pattern by this number.");
gd.addCheckbox ("Normalize", normalize, "Make sum of all pixel equal to 1.0");
gd.addMessage("--- obscured round objects, trying halves ---");
......@@ -3560,11 +3980,15 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{
if (gd.wasCanceled()) return;
half_size= (int) gd.getNextNumber();
half_pix= gd.getNextBoolean();
// evening_mode= gd.getNextBoolean();
radius= gd.getNextNumber();
edge= gd.getNextNumber();
radius_in= gd.getNextNumber();
edge_in= gd.getNextNumber();
scale_in= gd.getNextNumber();
radius_out= gd.getNextNumber();
edge_out= gd.getNextNumber();
scale_out= gd.getNextNumber();
scale= gd.getNextNumber();
normalize= gd.getNextBoolean();
......@@ -3577,14 +4001,17 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{
double [] kernel = getConvolutionKernel();
int size = 2*half_size;
double [] pattern = generateDoubleCircularPattern (
double [] pattern = generateTrippleCircularPattern( // generateDoubleCircularPattern (
half_size, // int half_size,
half_pix, // boolean half_pix, // center between pixels
radius, // double radius,
edge, // double edge,
radius_in, // double radius_in, // if 0 - skip
edge_in, // double edge_in,
scale_in); // double scale_in)
scale_in, // double scale_in)
radius_out, // double radius_out, // if 0 - skip
edge_out, // double edge_out,
scale_out); // double scale_out)
if (normalize) {
double sum_pix = 0;
......@@ -3620,8 +4047,10 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{
}
}
String pattern_name = "patterns_r"+radius+"_e"+edge+"_ir"+radius_in+
"ie"+edge_in+"_is"+scale_in+"_h"+halves_number+"_w"+cut_frac+
String pattern_name = "patterns_r"+radius+"_e"+edge+
"_ir"+radius_in+ "_ie"+edge_in+ "_is"+scale_in+
"_or"+radius_out+"_oe"+edge_out+"_os"+scale_out+
"_h"+halves_number+"_w"+cut_frac+
"_s"+scale+ "_"+size+"x"+size;
String [] pattern_titles = new String [patterns.length];
pattern_titles[0] = "full";
......@@ -3726,6 +4155,44 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{
return pattern;
}
public static double [] generateTrippleCircularPattern (
int half_size,
boolean half_pix, // center between pixels
double radius,
double edge,
double radius_in, // if 0 - skip
double edge_in,
double scale_in,
double radius_out, // if 0 - skip
double edge_out,
double scale_out) {
double [] pattern = generateCircularPattern (
half_size, // int half_size,
half_pix, // boolean half_pix, // center between pixels
radius, // double radius,
edge); // double edge)
if (radius_in > 0) {
double [] pattern1 = generateCircularPattern (
half_size, // int half_size,
half_pix, // boolean half_pix, // center between pixels
radius_in, // double radius,
edge_in); // double edge)
for (int i = 0; i < pattern.length; i++) {
pattern[i] += scale_in*pattern1[i];
}
}
if (radius_out > 0) {
double [] pattern2 = generateCircularPattern (
half_size, // int half_size,
half_pix, // boolean half_pix, // center between pixels
radius_out, // double radius,
edge_out); // double edge)
for (int i = 0; i < pattern.length; i++) {
pattern[i] += scale_out*pattern2[i];
}
}
return pattern;
}
public static double [] generateCircularPattern (
int half_size,
......
package com.elphel.imagej.orthomosaic;
import java.awt.Point;
import java.awt.Rectangle;
import java.io.File;
import java.io.FileInputStream;
......@@ -12,6 +13,7 @@ import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
......@@ -19,6 +21,7 @@ import java.util.List;
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.TpTask;
......@@ -29,23 +32,39 @@ import com.elphel.imagej.tileprocessor.TDCorrTile;
import ij.ImagePlus;
import ij.ImageStack;
import ij.gui.PointRoi;
import ij.process.FloatPolygon;
public class OrthoMapsCollection implements Serializable{
private static final long serialVersionUID = 1L;
public static final String [] KEY_DIRS= {"rootDirectory", // from EyesisCorrectionParameters
"sourceDirectory","linkedModels","videoDirectory","x3dDirectory","resultsDirectory","scenesDirectory"};
"sourceDirectory","linkedModels","videoDirectory","x3dDirectory","resultsDirectory","scenesDirectory",
"kernelsDirectory", "patternsDirectory"};
OrthoMap [] ortho_maps;
transient HashMap<Double,Integer> map_index;
transient HashMap<String,Integer> map_index_string;
transient ArrayList<AltitudeMismatchKernel> kernels;
transient ArrayList<GroundObjectPattern> patterns;
private void writeObject(ObjectOutputStream oos) throws IOException { // ?
oos.defaultWriteObject();
oos.writeObject(AltitudeMismatchKernel.getKernelsDirectory());
oos.writeObject(kernels);
oos.writeObject(GroundObjectPattern.getPatternsDirectory());
oos.writeObject(patterns);
// oos.writeObject(path);
// ortho_maps is not transient
}
@SuppressWarnings("unchecked")
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
ois.defaultReadObject();
AltitudeMismatchKernel.setKernelsDirectory((String) ois.readObject());
kernels = (ArrayList<AltitudeMismatchKernel>) ois.readObject();
GroundObjectPattern.setPatternsDirectory((String) ois.readObject());
patterns = (ArrayList<GroundObjectPattern>) ois.readObject();
reindex(); // will recreate map_index, map_index_string
// lla is not transient
}
......@@ -54,20 +73,59 @@ public class OrthoMapsCollection implements Serializable{
String path) {
if (path.endsWith(".list")) {
String[] scenes0 = new String[1];
String[] paths = getPathsAndScenesFromSourceList(path, scenes0);
kernels = new ArrayList<AltitudeMismatchKernel>();
patterns = new ArrayList<GroundObjectPattern>();
String[] paths = getPathsAndScenesFromSourceList(
path,
scenes0,
kernels, // ArrayList<AltitudeMismatchKernel> kernels,
patterns); // ArrayList<GroundObjectPattern> patterns);
String scenes_path = scenes0[0];
double [][] affine = {{1,0,0},{0,1,0}}; // maybe later calculate from mage_enuatr
ortho_maps = new OrthoMap[paths.length];
for (int n = 0; n < ortho_maps.length; n++) {
String name = OrthoMap.getNameFromPath(paths[n]);
// String scene_path = getSceneDir(scenes_path, name);
ortho_maps[n] = new OrthoMap(paths[n]); // , scene_path);
ortho_maps[n].setAffine(affine);
}
} else {
System.out.println("OrthoMapsCollection(): path should end with \".list\"");
}
Arrays.sort(ortho_maps);
reindex();
}
// add update kernels, update patterns
public void updateKernels (
String path) {
if (path.endsWith(".list")) {
kernels = new ArrayList<AltitudeMismatchKernel>();
getPathsAndScenesFromSourceList(
path,
null,
kernels, // ArrayList<AltitudeMismatchKernel> kernels,
null); // ArrayList<GroundObjectPattern> patterns);
} else {
System.out.println("updateKernels(): path should end with \".list\"");
}
}
public void updatePatterns (
String path) {
if (path.endsWith(".list")) {
patterns = new ArrayList<GroundObjectPattern>();
getPathsAndScenesFromSourceList(
path,
null,
null, // ArrayList<AltitudeMismatchKernel> kernels,
patterns); // ArrayList<GroundObjectPattern> patterns);
} else {
System.out.println("updatePatterns(): path should end with \".list\"");
}
}
static String getSceneDir(String scenes_path, String name) {
File [] scene_dirs = (new File(scenes_path)).listFiles(); // may contain non-directories, will be filtered by filterScenes
......@@ -125,16 +183,27 @@ public class OrthoMapsCollection implements Serializable{
for (int n = 0; n < ortho_maps.length; n++) {
ortho_maps[n].updateLLA();
}
updateAGL();
}
public void updateAGL() {
for (int n = 0; n < ortho_maps.length; n++) {
ortho_maps[n].agl = Double.NaN;
ortho_maps[n].agl = ortho_maps[n].getAGL();
}
}
/*
public static String[] getPathsFromSorceList(
String files_list) {
return getPathsAndScenesFromSourceList(files_list, null);
}
*/
public static String[] getPathsAndScenesFromSourceList(
String files_list,
String[] scenes0) {
String[] scenes0,
ArrayList<AltitudeMismatchKernel> kernels,
ArrayList<GroundObjectPattern> patterns) {
List<String> lines;
List<String> rel_files = new ArrayList<String>();
Path seq_path = Paths.get(files_list);
......@@ -160,6 +229,22 @@ public class OrthoMapsCollection implements Serializable{
}
System.out.println("*********** Unknown SET: "+tokens[1]+" in line: "+line);
}
} else if ((tokens.length > 0) && (tokens[0].toUpperCase().equals("KERNEL"))) {
if ((kernels != null) && (tokens.length > 4)) {
kernels.add(new AltitudeMismatchKernel(
tokens[1],
Integer.parseInt(tokens[2]),
Double.parseDouble(tokens[3]),
Double.parseDouble(tokens[4])));
}
} else if ((tokens.length > 0) && (tokens[0].toUpperCase().equals("PATTERN"))) {
if ((patterns != null) && (tokens.length > 4)) {
patterns.add(new GroundObjectPattern(
tokens[1],
Integer.parseInt(tokens[2]),
tokens[3],
LocalDateTime.parse(tokens[4])));
}
} else if ((tokens.length == 1) && (tokens[0].length() > 0)) {
rel_files.add(tokens[0]);
}
......@@ -185,6 +270,21 @@ public class OrthoMapsCollection implements Serializable{
scenes0[0]=(base_path.resolve(Paths.get(dir_map.get("scenesDirectory")))).toString();
}
}
String kernelsDirectory = base_path.toString();
// set sourceDirectory:
if (dir_map.containsKey("kernelsDirectory")) {
kernelsDirectory=(base_path.resolve(Paths.get(dir_map.get("kernelsDirectory")))).toString();
}
AltitudeMismatchKernel.setKernelsDirectory(kernelsDirectory);
String patternsDirectory = base_path.toString();
// set sourceDirectory:
if (dir_map.containsKey("patternsDirectory")) {
patternsDirectory=(base_path.resolve(Paths.get(dir_map.get("patternsDirectory")))).toString();
}
GroundObjectPattern.setPatternsDirectory(patternsDirectory);
return paths;
}
......@@ -197,8 +297,9 @@ public class OrthoMapsCollection implements Serializable{
* @return {{min_x,max_x},{min_y, max_y}} relative to the origin of the first image
*/
public int [][] getBoundsPixels(
int zoom_level){ // maps[0] as a reference
double [][] bounds_meters = getBoundsMeters();
int zoom_level, // maps[0] as a reference
int [] indices) {
double [][] bounds_meters = getBoundsMeters(indices);
double pix_size = OrthoMap.getPixelSizeMeters (zoom_level);
int [][] bounds_pix = new int[2][2];
for (int n = 0; n < bounds_pix.length; n++) {
......@@ -207,13 +308,17 @@ public class OrthoMapsCollection implements Serializable{
}
return bounds_pix;
}
public int [][] getBoundsPixels(
int zoom_level) { // maps[0] as a reference
return getBoundsPixels(zoom_level, null);
}
/**
* Get rectified bounds of all provided ortho images relative to the origin (vertical
* point) of the first one in meters
* @return {{min_x,max_x},{min_y,max_y}} bounds that include all provided maps
* relative to the origin (vertical point) of the first image
*/
/*
public double [][] getBoundsMeters(){ // maps[0] as a reference
double [][] bounds = ortho_maps[0].getBoundsMeters(true);
for (int nmap = 1; nmap < ortho_maps.length; nmap++) {
......@@ -227,6 +332,39 @@ public class OrthoMapsCollection implements Serializable{
}
return bounds;
}
*/
public double [][] getBoundsMeters(){ // maps[0] as a reference
return getBoundsMeters(null);
}
/**
* Get rectified bounds of all (if indices==null) or selected by indices[]
* provided ortho images relative to the origin (vertical
* point) of the first one in meters
* @param indices - image indices to process or null to process all images
* @return {{min_x,max_x},{min_y,max_y}} bounds that include all provided maps
* relative to the origin (vertical point) of the first image
*/
public double [][] getBoundsMeters(int [] indices){ // maps[0] as a reference
if (indices == null) {
indices = new int [ortho_maps.length];
for (int i = 0; i < indices.length; i++) {
indices[i] = i;
}
}
double [][] bounds = ortho_maps[indices[0]].getBoundsMeters(true);
for (int imap = 1; imap < indices.length; imap++) {
int nmap = indices[imap];
double [][] bounds_other = ortho_maps[nmap].getBoundsMeters(true);
double [] enuOffset = ortho_maps[indices[0]].enuOffsetTo(ortho_maps[nmap]);
double [] rd = {enuOffset[0], -enuOffset[1]}; // {right,down} of the image
for (int n = 0; n < bounds.length; n++) {
bounds[n][0] = Math.min(bounds[n][0],bounds_other[n][0]+ rd[n]);
bounds[n][1] = Math.max(bounds[n][1],bounds_other[n][1]+ rd[n]);
}
}
return bounds;
}
/**
* Get rectified overlap bounds of two provided ortho images relative to the origin (vertical
......@@ -265,11 +403,14 @@ public class OrthoMapsCollection implements Serializable{
boolean use_alt,
boolean show_centers,
int zoom_level,
int temp_mode, // 0 - do nothing, 1 - equalize average,2 - try to correct
int [] origin){
return renderMulti (
title, // String title,
use_alt, //boolean use_alt,
use_alt, // boolean use_alt,
null, // int [] indices, // null or which indices to use (normally just 2 for pairwise comparison)
false, // bounds_to_indices, // boolean bounds_to_indices,
temp_mode, // int temp_mode, // 0 - do nothing, 1 - equalize average,2 - try to correct
null, // double [][][] affines, // null or [indices.length][2][3]
null, // FineXYCorr warp,
show_centers, // boolean show_centers,
......@@ -281,16 +422,20 @@ public class OrthoMapsCollection implements Serializable{
String title,
boolean use_alt,
int [] indices, // null or which indices to use (normally just 2 for pairwise comparison)
boolean bounds_to_indices,
int temp_mode, // 0 - do nothing, 1 - equalize average,2 - try to correct
double [][][] affines, // null or [indices.length][2][3]
FineXYCorr warp, // use for a single pair only
boolean show_centers,
int zoom_level,
int [] origin){
int [] wh = new int[2];
// boolean bounds_to_indices = true;
double [][] centers = new double [(indices !=null)? indices.length: ortho_maps.length][];
float [][] multi = renderMulti (
use_alt, // boolean use_alt,
indices, // int [] indices, // null or which indices to use (normally just 2 for pairwise comparison)
bounds_to_indices, // boolean bounds_to_indices,
affines, // double [][][] affines, // null or [indices.length][2][3]
warp, // FineXYCorr warp,,
zoom_level, // int zoom_level,
......@@ -312,9 +457,18 @@ public class OrthoMapsCollection implements Serializable{
int map0= (indices != null)? indices[0] : 0;
if (!use_alt) {
for (int n = 1; n < num_images; n++) {
//temp_mode, // 0 - do nothing, 1 - equalize average,2 - try to correct
int mapn= (indices != null)? indices[n] : n;
double offs = ortho_maps[mapn].getTemperature()-ortho_maps[mapn].getAveragePixel()
double offs = 0;
switch (temp_mode) {
case 1:
offs = ortho_maps[map0].getAveragePixel()-ortho_maps[mapn].getAveragePixel();
break;
case 2:
offs = ortho_maps[mapn].getTemperature()-ortho_maps[mapn].getAveragePixel()
-(ortho_maps[map0].getTemperature()-ortho_maps[map0].getAveragePixel());
break;
}
float foffs= (float) offs;
for (int i = 0; i < extra_multi[n].length; i++) if (!Float.isNaN(extra_multi[n][i])) {
extra_multi[n][i]+= foffs;
......@@ -342,7 +496,9 @@ public class OrthoMapsCollection implements Serializable{
roi.setOptions("label");
imp.setRoi(roi);
}
imp.show(); // debugging
return imp;
}
......@@ -367,6 +523,7 @@ public class OrthoMapsCollection implements Serializable{
return renderMulti (
use_alt, // boolean use_alt,
null, // int [] indices,
false, // boolean bounds_to_indices,
null, // double [][][] affines,
null, // FineXYCorr warp,
zoom_level, // int zoom_level,
......@@ -374,9 +531,11 @@ public class OrthoMapsCollection implements Serializable{
origin, // int [] origin, // maps[0] as a reference
centers); // double [][] centers)
}
public float [][] renderMulti (
boolean use_alt,
int [] indices, // null or which indices to use (normally just 2 for pairwise comparison)
boolean bounds_to_indices,
double [][][] affines, // null or [indices.length][2][3]
FineXYCorr warp,
int zoom_level,
......@@ -385,7 +544,8 @@ public class OrthoMapsCollection implements Serializable{
double [][] centers){
final int dbg_x=2783, dbg_y=-5228;
int [][] bounds = getBoundsPixels( // should be for rectified, {-bounds[0][0], -bounds[0][1]} - exact center
zoom_level);
zoom_level,
bounds_to_indices? indices: null);
int width = bounds[0][1] - bounds[0][0]; // bounds[x][0] - negative
int height = bounds[1][1] - bounds[1][0];
if (wh != null) {
......@@ -416,7 +576,7 @@ public class OrthoMapsCollection implements Serializable{
// ortho_maps[nmap].getBoundsMeters(true, affines[indx]): // use provided affine
// ortho_maps[nmap].getBoundsMeters(true);
double [][] mbounds = ortho_maps[nmap].getBoundsMeters(true); // keep original bounds
double [] enu_offset = ortho_maps[0].enuOffsetTo(ortho_maps[nmap]);
double [] enu_offset = ortho_maps[indices[0]].enuOffsetTo(ortho_maps[nmap]);
final double [] scaled_out_center = { // xy center to apply affine to
-bounds[0][0] + scale * enu_offset[0],
-bounds[1][0] - scale * enu_offset[1]};
......@@ -496,6 +656,125 @@ public class OrthoMapsCollection implements Serializable{
return fpixels;
}
public double [][] renderMultiDouble (
boolean use_alt,
int [] indices, // null or which indices to use (normally just 2 for pairwise comparison)
boolean bounds_to_indices,
double [][][] affines, // null or [indices.length][2][3]
FineXYCorr warp,
int zoom_level,
int [] wh,
int [] origin, // maps[0] as a reference
double [][] centers){
final int dbg_x=2783, dbg_y=-5228;
int [][] bounds = getBoundsPixels( // should be for rectified, {-bounds[0][0], -bounds[0][1]} - exact center
zoom_level,
bounds_to_indices? indices: null);
int width = bounds[0][1] - bounds[0][0]; // bounds[x][0] - negative
int height = bounds[1][1] - bounds[1][0];
if (wh != null) {
wh[0] = width;
wh[1] = height;
}
if (origin != null) {
origin[0] = -bounds[0][0];
origin[1] = -bounds[1][0];
}
if (indices == null) {
indices = new int [ortho_maps.length];
for (int i = 0; i < indices.length; i++) {
indices[i] = i;
}
}
final double [][] dpixels = new double [indices.length][width * height];
for (int indx = 0; indx < indices.length; indx++) { //:indices) { // = 0; nmap< ortho_maps.length; nmap++) {
final int findx= indx;
final int nmap = indices[indx]; // nmap;
Arrays.fill(dpixels[findx], Float.NaN);
final double scale = 1.0/OrthoMap.getPixelSizeMeters(zoom_level);
final double src_scale = 1.0/OrthoMap.getPixelSizeMeters(ortho_maps[nmap].orig_zoom_level); // pix per meter
// metric bounds of the rectified image relative to its origin
double [][] mbounds = ortho_maps[nmap].getBoundsMeters(true); // keep original bounds
double [] enu_offset = ortho_maps[indices[0]].enuOffsetTo(ortho_maps[nmap]);
final double [] scaled_out_center = { // xy center to apply affine to
-bounds[0][0] + scale * enu_offset[0],
-bounds[1][0] - scale * enu_offset[1]};
if (centers != null) {
centers[findx] = scaled_out_center;
}
final int [][] obounds = new int [2][2]; // output (rectified, combined) image bounds, relative to thje top-left
for (int n = 0; n< 2; n++) {
obounds[n][0] = (int) Math.floor(scaled_out_center[n] + scale*mbounds[n][0]);
obounds[n][1] = (int) Math.ceil (scaled_out_center[n] + scale*mbounds[n][1]);
}
// Output window size (keep original affine - OK if will not exactly fit)
final int ownd_width = obounds[0][1] - obounds[0][0];
final int ownd_height = obounds[1][1] - obounds[1][0];
final int ownd_len = ownd_width * ownd_height;
double [][] src_bounds=ortho_maps[nmap].getBoundsMeters (true); // using original affines
final double [] src_center = {-src_bounds[0][0],-src_bounds[1][0]}; // x,y center offset in the source image
final double [][] affine = (affines !=null) ? affines[indx]: ortho_maps[nmap].affine; // only here use provided
final int src_width = use_alt? ortho_maps[nmap].getAltData().width: ortho_maps[nmap].getImageData().width;
final int src_height = use_alt? ortho_maps[nmap].getAltData().height : ortho_maps[nmap].getImageData().height;
final float [] src_img = use_alt? ortho_maps[nmap].getAltData().data : ortho_maps[nmap].getImageData().data;
if ((indx==0) && (warp != null)) { // set center from the first image
warp.setRender(
zoom_level, // int zoom_lev,
scaled_out_center[0], // double px0, // in render pixels
scaled_out_center[1]); // // double py0);
}
final Rectangle warp_woi =((indx==1) && (warp != null))? warp.getRenderWOI():null;
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() {
for (int nPix = ai.getAndIncrement(); nPix < ownd_len; nPix = ai.getAndIncrement()) {
int opX = nPix % ownd_width + obounds[0][0]; // absolute output pX, pY
int opY = nPix / ownd_width + obounds[1][0];
double dX = (opX - scaled_out_center[0]) /scale; // in original image scale
double dY = (opY - scaled_out_center[1]) /scale;
double [] xy_src = { // pixels of the source image
src_scale * (affine[0][0]*dX + affine[0][1]*dY + affine[0][2] + src_center[0]),
src_scale * (affine[1][0]*dX + affine[1][1]*dY + affine[1][2] + src_center[1])};
// limit to the source image
if ((warp_woi != null) && (warp_woi.contains(opX,opY))) {
if ((opX==dbg_x) && (opY==dbg_y)) {
System.out.println("opX="+opX+", opy="+opY);
}
double [] dxy = warp.getWarp(opX,opY);
xy_src[0] += dxy[0];
xy_src[1] += dxy[1];
}
if ((xy_src[0] >= 0) && (xy_src[0] < (src_width-1)) &&
(xy_src[1] >= 0) && (xy_src[1] < (src_height-1))) {
int [] ixy_src = {(int) Math.floor(xy_src[0]), (int)Math.floor(xy_src[1]) };
double [] kxy = {xy_src[0]-ixy_src[0], xy_src[1]-ixy_src[1]};
int indx00 = ixy_src[0] + ixy_src[1] * src_width;
double d00 = src_img[indx00];
if (!Double.isNaN(d00)) {
double d01 = src_img[indx00 + 1];
double d10 = src_img[indx00 + src_width];
double d11 = src_img[indx00 + src_width + 1];
double d = d00*(1.0 - kxy[0])*(1.0 - kxy[1])+
d01* kxy[0] *(1.0 - kxy[1])+
d10*(1.0 - kxy[0])* kxy[1]+
d11* kxy[0] * kxy[1];
dpixels[findx][opX + opY*width] = (float) d;
}
}
}
}
};
}
ImageDtt.startAndJoin(threads);
}
return dpixels;
}
public int getIndex(String sindx) {
return map_index_string.get(sindx);
}
......@@ -1313,5 +1592,361 @@ public class OrthoMapsCollection implements Serializable{
return vf_out;
}
public ImagePlus patterMatchDualWrap (
int [] indices, // null or which indices to use (normally just 2 for pairwise comparison)
double [][][] affines, // null or [indices.length][2][3]
FineXYCorr warp) { // use for a single pair only
double [] min_corrs = {0.01,0.005}; // morning/evening
double phaseCoeff = 0.5;
double min_corr_full_rel = 0.75;
double full_preference = 1.4;
double max_min_ratio = 3.0;
boolean combine_full = true;
int corr_size = 128; // pix
double adv_radius = 30.0; // cm
String object_type = "TM62";
double scale_warp = 1.0; // use 4?
int debugLevel = 1;
GenericJTabbedDialog gd = new GenericJTabbedDialog("Correlate two images with patterns",1200,500);
gd.addNumericField("Minimal correlation, first", min_corrs[0], 5,7,"","Minimal correlation value to keep, first scene.");
gd.addNumericField("Minimal correlation, second", min_corrs[1], 5,7,"","Minimal correlation value to keep, secondscene.");
gd.addNumericField("Phase correlation coefficient", phaseCoeff, 3,7,"","1.0 - pure phase correlation, 0.0 - regular correlation.");
gd.addNumericField("Minimal relative full correlation", min_corr_full_rel, 5,7,"","Minimal relative correlation value with full circular pattern to keep.");
gd.addNumericField("Prefer full pattern", full_preference, 3,7,"","Prefer full pattern to partial; scale by this value.");
gd.addNumericField("Max/min halves ratio", max_min_ratio, 3,7,"","Ratio of halves best/worst to use half-pattern.");
gd.addCheckbox ("Combine with full pattern", combine_full, "Multiply by normalized full pattern correlation maximum.");
gd.addNumericField("Correlation size", corr_size, 0,4,"pix", "Should be power of 2.");
gd.addNumericField("Adversarial distance", adv_radius, 1,6,"pix", "Suppress weaker if they have closer strong ones.");
gd.addStringField ("Object type", object_type, 20,"Object type/name.");
gd.addNumericField("Scale warp", scale_warp, 1,6,"x", "Scale second image warping.");
gd.addNumericField("Debug level", debugLevel, 0,4,"", "Debug level.");
gd.showDialog();
if (gd.wasCanceled()) return null;
min_corrs[0]= gd.getNextNumber();
min_corrs[1]= gd.getNextNumber();
phaseCoeff= gd.getNextNumber();
min_corr_full_rel= gd.getNextNumber();
full_preference= gd.getNextNumber();
max_min_ratio= gd.getNextNumber();
combine_full = gd.getNextBoolean();
corr_size= (int) gd.getNextNumber();
adv_radius= gd.getNextNumber();
object_type= gd.getNextString();
scale_warp = gd.getNextNumber();
debugLevel= (int) gd.getNextNumber();
return patternMatchDual (
indices, // int [] indices, // null or which indices to use (normally just 2 for pairwise comparison)
affines, // double [][][] affines, // null or [indices.length][2][3]
warp, // FineXYCorr warp);
min_corrs, // double [] min_corrs, // one per
phaseCoeff, // double phaseCoeff,
min_corr_full_rel, // double min_corr_full_rel,
full_preference, // double full_preference,
max_min_ratio, // double max_min_ratio,
combine_full, // boolean combine_full,
corr_size, // int corr_size,
adv_radius, // double adv_radius, //
object_type, // String object_type,
scale_warp, // double scale_warp,
debugLevel); // int debugLevel)
}
public ImagePlus patternMatchDual (
int [] indices, // null or which indices to use (normally just 2 for pairwise comparison)
double [][][] affines, // null or [indices.length][2][3]
FineXYCorr warp, // use for a single pair only
double [] min_corrs, // one per
double phaseCoeff,
double min_corr_full_rel,
double full_preference,
double max_min_ratio,
boolean combine_full,
int corr_size,
double adv_radius, //
String object_type,
double scale_warp,
int debugLevel) {
warp.scale_warp = scale_warp;
int corr_radius = (int) (Math.sqrt(0.5)* adv_radius) -1 ;
// String title_match = OrthoMap.removeKnownExtension(imp_src.getTitle())+"-PATTERN_CORRS"
int min_zoom_lev = Math.min(
ortho_maps[indices[0]].getOriginalZoomLevel(),
ortho_maps[indices[1]].getOriginalZoomLevel());
int max_zoom_lev = Math.max(
ortho_maps[indices[0]].getOriginalZoomLevel(),
ortho_maps[indices[1]].getOriginalZoomLevel());
int zoom_level = min_zoom_lev;
int [] wh = new int[2];
int [] origin = new int[2];
double [][] centers = new double [indices.length][];
double [][] dmulti = renderMultiDouble (
false, // boolean use_alt,
indices, // int [] indices, // null or which indices to use (normally just 2 for pairwise comparison)
true, // boolean bounds_to_indices,
affines, // double [][][] affines, // null or [indices.length][2][3]
warp, // FineXYCorr warp,,
zoom_level, // int zoom_level,
wh, // int [] wh,
origin, // int [] origin){ // maps[0] as a reference
centers); // double [][] centers)
int width = wh[0];
int height = wh[1];
double [][] src_marks = null;
/*
PointRoi pRoi = (PointRoi) imp_src.getRoi();
FloatPolygon fp = pRoi.getContainedFloatPoints();
if (fp != null) {
src_marks = new double[fp.npoints][2];
for (int i = 0; i < src_marks.length; i++) {
src_marks[i][0] = fp.xpoints[i];
src_marks[i][1] = fp.ypoints[i];
}
}
*/
// double [] agls = new double[indices.length];
// LocalDateTime [] dts = new LocalDateTime[indices.length];
/*
int [] zoom_levs = new int[indices.length];
for (int scene_num = 0; scene_num < indices.length; scene_num++) {
// agls[n] = ortho_maps[indices[n]].getAGL();
// dts[n] = ortho_maps[indices[n]].getLocalDateTime();
zoom_levs[scene_num]= ortho_maps[indices[scene_num]].getOriginalZoomLevel();
}
*/
double [][][] patterns_all = new double [indices.length][][];
double [][] kernels_all = new double [indices.length][];
GroundObjectPattern [] gops = new GroundObjectPattern[indices.length];
AltitudeMismatchKernel[] ak = new AltitudeMismatchKernel[indices.length];
int [] pattern_sizes = new int[indices.length];
String [][] pattern_labels = new String[indices.length][];
double [][][] corrs_out = new double[indices.length][][];
double [][][] convolve_out = new double[indices.length][][];
double [][][] corr_patterns = new double[indices.length][][];
double [][][] filtered_corrs = new double[indices.length][][];
int [] zoomout = new int[indices.length];
ImagePlus [] imp_corrs = new ImagePlus[indices.length];
double [][] bestcorr = new double[indices.length][];
double [][] fullcorr = new double[indices.length][];
int [][] bestpatt = new int [indices.length][];
double [] min_corrs_full = new double[indices.length];
ArrayList<ArrayList<Point>> object_lists = new ArrayList<ArrayList<Point>>();
ImagePlus [] imps_best = new ImagePlus[indices.length];
ImagePlus [] imps_bestpatt = new ImagePlus[indices.length];
double [][] convolved_merged = new double[indices.length][];
ImagePlus [] imps_patt_corrs = new ImagePlus[indices.length];
ImagePlus [] imps_patt_conv = new ImagePlus[indices.length];
for (int scene_num = 0; scene_num < indices.length; scene_num++) {
gops[scene_num] = GroundObjectPattern.getPattern(
object_type, // String object_type,
ortho_maps[indices[scene_num]].getLocalDateTime(), // LocalDateTime utcDateTime,
patterns); // ArrayList<GroundObjectPattern> patterns)
ImagePlus imp_pattern = new ImagePlus(gops[scene_num].getPatternPath());
pattern_sizes[scene_num] = imp_pattern.getWidth();
if (pattern_sizes[scene_num] == 0) {
System.out.println("testPatternCorrelate(): pattern \""+gops[scene_num].getPatternPath()+"\" is not found.");
return null;
}
ImageStack stack_pattern = imp_pattern.getStack();
int nSlices = stack_pattern.getSize();
patterns_all[scene_num] = new double[nSlices][];
pattern_labels[scene_num] = new String[nSlices];
for (int k = 0; k < patterns_all[scene_num].length; k++) {
pattern_labels[scene_num][k]=stack_pattern.getShortSliceLabel(k+1);
float [] fpixels_pattern = (float[]) stack_pattern.getPixels(k+1);
patterns_all[scene_num][k]=new double[fpixels_pattern.length];
for (int i = 0; i < fpixels_pattern.length; i++) {
patterns_all[scene_num][k][i] = fpixels_pattern[i];
}
}
ak[scene_num] = AltitudeMismatchKernel.getKernel(
gops[scene_num].getZoomLevel(), // zoom_level, // int zoom_level,
gops[scene_num].getAGL(), // double agl_from,
ortho_maps[indices[scene_num]].getAGL(), // double agl_to,
kernels); // ArrayList<AltitudeMismatchKernel> kernels)
if (ak[scene_num] != null) {
String kernel_path = ak[scene_num].getKernelPath();
ImagePlus imp_kernel = new ImagePlus(kernel_path);
if (imp_kernel.getWidth() > 0) {
float [] kernel_pixels = (float[]) imp_kernel.getProcessor().getPixels();
kernels_all[scene_num] = new double[kernel_pixels.length];
for (int i = 0; i < kernel_pixels.length; i++) {
kernels_all[scene_num][i] = kernel_pixels[i];
}
}
}
corrs_out[scene_num] = new double[patterns_all[scene_num].length][];
convolve_out[scene_num] = new double[patterns_all[scene_num].length][];
corr_patterns[scene_num] = new double[patterns_all[scene_num].length][];
zoomout[scene_num] = 1;
for (int i = zoom_level; i < gops[scene_num].getZoomLevel();i++) {
zoomout[scene_num] *= 2;
}
for (int n = 0; n < patterns_all[scene_num].length; n++) {
corr_patterns[scene_num][n] = OrthoMap.patternZoomCropPad(
patterns_all[scene_num][n], // double [] pattern,
pattern_sizes[scene_num], // int pattern_size,
corr_size, // int size,
zoomout[scene_num], // int zoomout,
false); // out_normalize); // boolean normalize)
if (kernels_all[scene_num] != null) {
corr_patterns[scene_num][n] = OrthoMap.convolveWithKernel(
corr_patterns[scene_num][n], // final double [] data,
kernels_all[scene_num], // final double [] kernel,
corr_size); // final int width)
}
corrs_out[scene_num][n]= OrthoMap.correlateWithPattern(
dmulti[scene_num], // final double [] data,
width, // final int width,
corr_size, // final int psize, // power of 2, such as 64
corr_patterns[scene_num][n], // final double [] pattern, // [psize*psize]
false, // final boolean convolve, // convolve, not correlate
phaseCoeff, // final double phaseCoeff,
debugLevel); // final int debugLevel) {
}
String [] patt_titles = new String[corrs_out[scene_num].length];
for (int i = 0; i < patt_titles.length; i++) {
patt_titles[i] = "patt_"+i;
}
imp_corrs[scene_num] = ShowDoubleFloatArrays.makeArrays(
corrs_out[scene_num],
width,
height,
ortho_maps[indices[scene_num]].getName()+"-PATTERN_CORRS",
patt_titles); // test_titles,
if (src_marks != null) {
PointRoi roi = new PointRoi();
roi.setOptions("label");
for (int i = 0; i < src_marks.length; i++) {
roi.addPoint(src_marks[i][0],src_marks[i][1]); // ,1);
}
imp_corrs[scene_num].setRoi(roi);
}
imp_corrs[scene_num].show();
bestcorr[scene_num] = new double [dmulti[scene_num].length];
fullcorr[scene_num] = new double [dmulti[scene_num].length];
bestpatt[scene_num] = new int [bestcorr[scene_num].length];
min_corrs_full[scene_num] = min_corrs[scene_num] * min_corr_full_rel;
// TODO: scale adv_radius, corr_radius
object_lists.add(OrthoMap.combineDirCorrs (
corrs_out[scene_num], // final double [][] corrs,
width, // final int width,
fullcorr[scene_num], // final double [] fullcorr_in,
bestcorr[scene_num], // final double [] bestcorr_in
bestpatt[scene_num], // final int [] bestpatt_in,
min_corrs[scene_num], // final double min_corr,
min_corrs_full[scene_num], // final double min_corr_full,
full_preference, // final double full_preference,
max_min_ratio, // final double max_min_ratio,
combine_full, // final boolean combine_full, // multiply by normalized full pattern correlation maximum
adv_radius, // final double adv_radius,
corr_radius)); // final int corr_radius)
System.out.println("patternMatchDual(): Found "+object_lists.get(scene_num).size()+" candidates for scene #"+scene_num);
imps_best[scene_num] = ShowDoubleFloatArrays.makeArrays(
bestcorr[scene_num],
width,
height,
ortho_maps[indices[scene_num]].getName()+"-BEST_CORR");
double [] dbg_bestpatt = new double [bestpatt[scene_num].length];
Arrays.fill(dbg_bestpatt, Double.NaN);
for (int i = 0; i <bestpatt[scene_num].length; i++) {
if (bestpatt[scene_num][i] >=0) {
dbg_bestpatt[i] = bestpatt[scene_num][i];
}
}
imps_bestpatt[scene_num] = ShowDoubleFloatArrays.makeArrays(
dbg_bestpatt,
width,
height,
ortho_maps[indices[scene_num]].getName()+"-BEST_PATT");
if (src_marks != null) {
PointRoi roi = new PointRoi();
roi.setOptions("label");
for (int i = 0; i < src_marks.length; i++) {
roi.addPoint(src_marks[i][0],src_marks[i][1]); // ,1);
}
imps_best[scene_num].setRoi(roi);
imps_bestpatt[scene_num].setRoi(roi);
}
imps_best[scene_num].show();
imps_bestpatt[scene_num].show();
filtered_corrs[scene_num]= OrthoMap.splitPattenCorrelations(
corr_patterns[scene_num].length, // final int num_patterns,
bestcorr[scene_num], // final double [] bestcorr,
bestpatt[scene_num]); // final int [] bestpatt)
for (int n = 0; n < corr_patterns[scene_num].length; n++) {
convolve_out[scene_num][n]= OrthoMap.correlateWithPattern(
filtered_corrs[scene_num][n],// corrs_out[n], // final double [] data,
width, // final int width,
corr_size, // final int psize, // power of 2, such as 64
corr_patterns[scene_num][n], // final double [] pattern, // [psize*psize]
true, // final boolean convolve, // convolve, not correlate
phaseCoeff, // final double phaseCoeff,
debugLevel); // final int debugLevel) {
}
convolved_merged[scene_num] = OrthoMap.mergePattenConvolutions(
convolve_out[scene_num]); // final double [][] convs)
String [] rslt_titles =new String [corr_patterns[scene_num].length+2];
double [][] rslt = new double [corr_patterns[scene_num].length + 2][]; // ]{data, corr_out};
double [][] conv_rslt = new double [corr_patterns[scene_num].length + 2][]; // ]{data, corr_out};
rslt[0] = dmulti[scene_num];
conv_rslt[0] = dmulti[scene_num];
rslt_titles[0] = "original";
rslt[1] = bestcorr[scene_num];
conv_rslt[1] = convolved_merged[scene_num];
rslt_titles[1] = "combined";
for (int i = 0; i < corr_patterns[scene_num].length; i++) {
rslt[i+2] = filtered_corrs[scene_num][i];
conv_rslt[i+2] = convolve_out[scene_num][i];
rslt_titles[i+2] = "corr_"+pattern_labels[scene_num][i];
}
imps_patt_corrs[scene_num] = ShowDoubleFloatArrays.makeArrays(
rslt,
width,
height,
ortho_maps[indices[scene_num]].getName()+"-PATTERN_CORR",
rslt_titles);
if (src_marks != null) {
PointRoi roi = new PointRoi();
roi.setOptions("label");
for (int i = 0; i < src_marks.length; i++) {
roi.addPoint(src_marks[i][0],src_marks[i][1]); // ,1);
}
imps_patt_corrs[scene_num].setRoi(roi);
}
imps_patt_corrs[scene_num].show();
imps_patt_conv[scene_num] = ShowDoubleFloatArrays.makeArrays(
conv_rslt,
width,
height,
ortho_maps[indices[scene_num]].getName()+"-PATTERN_CORR_CONVOLVE_min_corr"+min_corrs[scene_num],
rslt_titles);
if (src_marks != null) {
PointRoi roi = new PointRoi();
roi.setOptions("label");
for (int i = 0; i < src_marks.length; i++) {
roi.addPoint(src_marks[i][0],src_marks[i][1]); // ,1);
}
imps_patt_conv[scene_num].setRoi(roi);
}
imps_patt_conv[scene_num].show();
System.out.println("patternMatchDual(): correlation done for scene "+scene_num);
}
System.out.println("patternMatchDual(): all correlations DONE");
return null;
}
}
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