Commit f97f3155 authored by Andrey Filippov's avatar Andrey Filippov

Implemented OrthoMapCollection class and possibility to save maps state

w/o images
parent 624f1144
......@@ -109,7 +109,7 @@ import com.elphel.imagej.ims.EventLogger;
import com.elphel.imagej.ims.Imx5;
import com.elphel.imagej.jp4.JP46_Reader_camera;
import com.elphel.imagej.lwir.LwirReader;
import com.elphel.imagej.orthomosaic.ComboMap;
import com.elphel.imagej.orthomosaic.OrthoMap;
import com.elphel.imagej.orthomosaic.ComboMatch;
import com.elphel.imagej.readers.ChangeImageResolution;
import com.elphel.imagej.readers.DumpImageMetadata;
......@@ -5732,7 +5732,7 @@ public class Eyesis_Correction implements PlugIn, ActionListener {
IJ.showMessage("Error", "No images selected");
return;
}
ComboMap.testVideo(imp_sel);
OrthoMap.testVideo(imp_sel);
}
}
......
......@@ -45,7 +45,8 @@ public class ComboMatch {
GPU_TILE_PROCESSOR = gpu_tile_processor;
// find -L /media/elphel/SSD3-4GB/lwir16-proc/berdich3/linked/linked_1697875868-1697879449-b/ -type f -name "*-GCORR-GEO.tiff" | sort > GCORR-GEO.list
String files_list_path = "/media/elphel/SSD3-4GB/lwir16-proc/ortho_videos/maps_berdich2.list";
String files_list_path = "/media/elphel/SSD3-4GB/lwir16-proc/ortho_videos/maps_03_short.list";
String orthoMapsCollection_path = "/media/elphel/SSD3-4GB/lwir16-proc/ortho_videos/ortho_maps_collection.data";
double [][][] image_enuatr = {{{0,0,0},{0,0,0}},{{0,0,0},{0,0,0}}};
int gpu_width= clt_parameters.imp.rln_gpu_width; // 3008;
......@@ -53,8 +54,13 @@ public class ComboMatch {
int zoom_lev = -4; // 0; // +1 - zoom in twice, -1 - zoom out twice
boolean use_alt = false;
boolean show_centers = true;
boolean use_saved_collection = false;
boolean save_collection = true;
GenericJTabbedDialog gd = new GenericJTabbedDialog("Set image pair",1200,800);
gd.addStringField ("Image list full path ", files_list_path, 180, "Image listfull path.");
gd.addStringField ("Image list full path", files_list_path, 180, "Image list full path.");
gd.addStringField ("Maps collection save path", orthoMapsCollection_path, 180, "Save path for serialized map collection data.");
gd.addCheckbox ("Use saved maps collection", use_saved_collection, "If false - use files list.");
gd.addCheckbox ("Save maps collection", save_collection, "If false - use files list.");
// for (int n = 0; n < image_paths_pre.length; n++) {
// gd.addStringField ("Image path "+n, image_paths_pre[n], 180, "Image "+n+" full path w/o ext");
// }
......@@ -81,11 +87,11 @@ public class ComboMatch {
gd.showDialog();
if (gd.wasCanceled()) return false;
files_list_path = gd.getNextString();
// for (int n = 0; n < image_paths_pre.length; n++) {
// image_paths_pre[n] = gd.getNextString();
// }
// image_paths_pre[0] = gd.getNextString();
// image_paths_pre[1] = gd.getNextString();
orthoMapsCollection_path = gd.getNextString();
use_saved_collection = gd.getNextBoolean();
save_collection = gd.getNextBoolean();
for (int n = 0; n < image_enuatr.length; n++) {
image_enuatr[n][0][0] = gd.getNextNumber();
image_enuatr[n][0][1] = gd.getNextNumber();
......@@ -98,58 +104,71 @@ public class ComboMatch {
gpu_width = (int) gd.getNextNumber();
gpu_height = (int) gd.getNextNumber();
ComboMap.setGPUWidthHeight(gpu_width,gpu_height);
OrthoMap.setGPUWidthHeight(gpu_width,gpu_height);
show_centers = gd.getNextBoolean();
use_alt = gd.getNextBoolean();
// ComboMap[] combo_maps = ComboMap.initializeComboMaps(
// full_paths); // String [] image_paths);
ComboMap[] combo_maps = ComboMap.initializeComboMaps(
files_list_path); // String [] image_paths);
String [] map_names = ComboMap.getNames(combo_maps);
OrthoMapsCollection maps_collection=null;
if (use_saved_collection) {
try {
maps_collection = OrthoMapsCollection.readOrthoMapsCollection (orthoMapsCollection_path);
} catch (ClassNotFoundException | IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
maps_collection = new OrthoMapsCollection(files_list_path); // should have ".list" extensiohn
}
String [] names = maps_collection.getNames();
// which pair to compare
int [] gpu_pair = {1,2};
String [] gpu_spair = {names[gpu_pair[0]],names[gpu_pair[1]]};
int [] origin = new int[2];
ImagePlus imp_img = ComboMap.renderMulti (
ImagePlus imp_img = maps_collection.renderMulti (
"multi_"+zoom_lev, // String title,
false, // boolean use_alt,
show_centers, // boolean show_centers,
combo_maps, // ComboMap [] maps,
zoom_lev, // int zoom_level,
origin); // int [] origin){
imp_img.show();
ImagePlus imp_alt = null;
if (use_alt) {
imp_alt =ComboMap.renderMulti (
imp_alt =maps_collection.renderMulti (
"multi_alt_"+zoom_lev, // String title,
true, // boolean use_alt,
show_centers, // boolean show_centers,
combo_maps, // ComboMap [] maps,
zoom_lev, // int zoom_level,
origin); // int [] origin){
imp_alt.show();
}
// which pair to compare
int [] gpu_pair = {1,2};
float [][] gpu_pair_img = new float [2][];
for (int n = 0; n < gpu_pair.length; n++) {
gpu_pair_img[n] = combo_maps[gpu_pair[n]].getPaddedGPU (
zoom_lev, // int zoom_level,
gpu_width, // int gpu_width,
gpu_height); // int gpu_height)
}
float [][] gpu_pair_img = maps_collection.getPaddedPairGPU(
gpu_spair, // String [] spair,
zoom_lev); // int zoom_lev);
ShowDoubleFloatArrays.showArrays(
gpu_pair_img,
gpu_width,
gpu_height,
true,
"gpu_pair-zoom"+zoom_lev+"-"+combo_maps[gpu_pair[0]].getName()+"-"+combo_maps[gpu_pair[1]].getName(),
map_names);
"gpu_pair-zoom"+zoom_lev+"-"+gpu_spair[0]+"-"+gpu_spair[1],
gpu_spair);
if (save_collection) {
try {
maps_collection.writeOrthoMapsCollection(orthoMapsCollection_path);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (debugLevel <1000) {
return true;
}
/* */
OrthoMap [] ortho_maps = maps_collection.getMaps(); // just temporarily
int [] widths = new int[imp_src.length];
int [] heights = new int[imp_src.length];
for (int n = 0; n < imp_src.length; n++) {
......@@ -178,8 +197,8 @@ public class ComboMatch {
}
/* */
if (show_centers) {
for (int n = 0; n < combo_maps.length; n++) {
combo_maps[n].getOriginalImage(true);
for (int n = 0; n < ortho_maps.length; n++) {
ortho_maps[n].getOriginalImage(true);
}
}
/*
......@@ -187,11 +206,12 @@ public class ComboMatch {
imp_src[n].show();
}
*/
if (combo_maps[0].orig_zoom_level != combo_maps[1].orig_zoom_level) {
if (ortho_maps[0].orig_zoom_level != ortho_maps[1].orig_zoom_level) {
System.out.println("openTestPairGps() does not yet handle different scales, aborting");
return false;
}
double [] enu_diff = combo_maps[0].enuOffsetTo(combo_maps[0]);
double [] enu_diff = ortho_maps[0].enuOffsetTo(ortho_maps[0]);
double pix_size1 = ElphelTiffReader.getPixelSize(imp_prop[0])[0];
double [] pix_shift = {enu_diff[0]/pix_size1, -enu_diff[1]/pix_size1}; // E->X, N->-Y
......
......@@ -4,6 +4,9 @@ import java.awt.Color;
import java.awt.Font;
import java.io.File;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
......@@ -39,28 +42,73 @@ import ij.process.FloatProcessor;
import ij.process.ImageConverter;
import ij.process.ImageProcessor;
public class ComboMap {
public class OrthoMap implements Comparable <OrthoMap>, Serializable{
private static final long serialVersionUID = 1L;
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 ALT_SUFFIX = "-ALT";
public final String name; // timestamp
public String path; // full path to the model directory (including /vXX?)
public double [] lla; // lat/long/alt
public LocalDateTime dt;
// public transient final String name; // timestamp
// public transient final double ts;
public transient String name; // timestamp
public transient double ts;
public transient String path; // full path to the model directory (including /vXX?)
public double [] lla; // lat/long/alt
public LocalDateTime dt;
// affine convert (input) rectified coordinates (meters) relative to vert_meters to source image
// coordinates relative to vert_meters
private double [][] affine = new double[][] {{1,0,0},{0,1,0}}; // relative to vert_meters[]
public double orig_pix_meters;
public double [] vert_meters; // offset of the image vertical in meters (scale-invariant)
public FloatImageData orig_image;
public FloatImageData alt_image;
public int orig_zoom_level;
public boolean orig_zoom_valid;
public double need_extra_zoom;
HashMap <Integer, FloatImageData> images;
public double [][] affine = new double[][] {{1,0,0},{0,1,0}}; // relative to vert_meters[]
public double orig_pix_meters;
public double [] vert_meters; // offset of the image vertical in meters (scale-invariant)
public transient FloatImageData orig_image;
public transient FloatImageData alt_image;
public int orig_zoom_level;
public boolean orig_zoom_valid;
public double need_extra_zoom;
transient HashMap <Integer, FloatImageData> images;
HashMap <Double, PairwiseOrthoMatch> pairwise_matches;
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject();
oos.writeObject(path);
// lla is not transient
// dt is not transient
// affine is not transient
// orig_pix_meters is not transient
// vert_meters is not transient
// orig_image does not need to be saved
// alt_image does not need to be saved
// orig_zoom_level is not transient
// orig_zoom_valid is not transient
// need_extra_zoom is not transient
// images is not saved
// pairwise_matches is not transient
}
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
ois.defaultReadObject();
path = (String) ois.readObject();
name = getNameFromPath(path);
ts= Double.parseDouble(name.replace("_", "."));
// lla is not transient
// dt is not transient
// affine is not transient
// orig_pix_meters is not transient
// vert_meters is not transient
// orig_image was not saved
// alt_image was not saved
images = new HashMap <Integer, FloatImageData>(); // field images was not saved
// pairwise_matches is not transient
// pairwise_matches = new HashMap<Double, PairwiseOrthoMatch>();
}
@Override
public int compareTo(OrthoMap otherPlayer) {
return Double.compare(ts, otherPlayer.ts);
}
public static void setGPUWidthHeight(
int width,
......@@ -83,9 +131,10 @@ public class ComboMap {
return path.substring(p1+1, p2);
}
public ComboMap (String path) {
public OrthoMap (String path) {
this.path = path;
name = getNameFromPath(path);
ts= Double.parseDouble(name.replace("_", "."));
Properties imp_prop = null;
try {
imp_prop = ElphelTiffReader.getTiffMeta(path);
......@@ -107,87 +156,13 @@ public class ComboMap {
orig_zoom_valid = FloatImageData.isZoomValid(orig_pix_meters);
need_extra_zoom = FloatImageData.needZoomIn(orig_pix_meters);
images = new HashMap <Integer, FloatImageData>();
pairwise_matches = new HashMap<Double, PairwiseOrthoMatch>();
}
public LocalDateTime getLocalDateTime() {
return dt;
}
public static ComboMap[] initializeComboMaps(
String [] image_paths) { // full paths, including ext
double [][] affine = {{1,0,0},{0,1,0}}; // maybe later calculate from mage_enuatr
ComboMap[] combo_maps = new ComboMap[image_paths.length];
for (int n = 0; n < combo_maps.length; n++) {
combo_maps[n] = new ComboMap(image_paths[n]);
combo_maps[n].setAffine(affine);
}
return combo_maps;
}
public static ComboMap[] initializeComboMaps(
String files_list) {
List<String> lines;
List<String> rel_files = new ArrayList<String>();
Path seq_path = Paths.get(files_list);
try {
lines = Files.readAllLines(seq_path, StandardCharsets.UTF_8);
// lines.stream().forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
return null;
}
Path base_path = seq_path.getParent();
HashMap<String,String> dir_map = new HashMap<String,String>();
for (String line:lines){
if (line.split("#").length > 0) {
String[] tokens = line.split("#")[0].trim().split("[\\s,;=]+");
if ((tokens.length > 2) && (tokens[0].toUpperCase().equals("SET"))) {
parse_set:
{
for (String dir_name:KEY_DIRS) if (dir_name.equals(tokens[1])) {
dir_map.put(dir_name,tokens[2]);
System.out.println("Parsed SET: "+tokens[1]+" in line: "+line);
break parse_set;
}
System.out.println("*********** Unknown SET: "+tokens[1]+" in line: "+line);
}
} else if ((tokens.length == 1) && (tokens[0].length() > 0)) {
rel_files.add(tokens[0]);
}
}
}
if (dir_map.containsKey("rootDirectory")) {
base_path=base_path.resolve(Paths.get(dir_map.get("rootDirectory")));
File base_dir = new File(base_path.toString());
// if (!base_dir.exists()) {
// base_dir.mkdirs();
// }
}
String sourceDirectory = base_path.toString();
// set sourceDirectory:
if (dir_map.containsKey("sourceDirectory")) {
sourceDirectory=(base_path.resolve(Paths.get(dir_map.get("sourceDirectory")))).toString();
}
Path source_path = Paths.get(sourceDirectory);
File source_dir = new File(source_path.toString());
String [] paths = new String[rel_files.size()];
for (int i = 0; i < paths.length; i++) {
paths[i] = (source_path.resolve(Paths.get(rel_files.get(i)))).toString();
}
return initializeComboMaps(paths);
}
public static String[] getNames(
ComboMap[] maps) {
String [] names = new String [maps.length];
for (int n = 0; n < maps.length; n++) {
names[n] = maps[n].getName();
}
return names;
}
public String getName() {
return name;
......@@ -293,7 +268,7 @@ public class ComboMap {
return null;
}
public double [] enuOffsetTo(ComboMap other) {
public double [] enuOffsetTo(OrthoMap other) {
return Imx5.enuFromLla(other.lla, lla);
}
......@@ -378,27 +353,6 @@ public class ComboMap {
return bounds_pix;
}
/**
* Get rectified bounds of all provided combo images relative to the origin (vertical
* point) of the first one in meters
* @param maps array of combo 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 static double [][] getBoundsMeters(ComboMap [] maps){ // maps[0] as a reference
double [][] bounds = maps[0].getBoundsMeters(true);
for (int nmap = 1; nmap < maps.length; nmap++) {
double [][] bounds_other = maps[nmap].getBoundsMeters(true);
double [] enuOffset = maps[0].enuOffsetTo(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;
}
/**
* In preparation for the GPU correlation (zoomed out images of the fixed
* size are calculated separately and have NaN for unused pixels.
......@@ -407,8 +361,8 @@ public class ComboMap {
* affine matrices for both images ([2][2][3]).
* The overlap area should fit into gpu_width, gpu_hight, the affine-generated
* image coordinates will be limited during TpTask[][] generation
* @param ref_map ComboMap instance for the reference map
* @param var_map ComboMap instance for the variable map instance
* @param ref_map OrthoMap instance for the reference map
* @param var_map OrthoMap instance for the variable map instance
* @param zoom_lev zoom level (0 - 1 pix=1cm, 1 - 1 pix = 0.5cm, -1 - 1 pix = 2 cm.
* @param var_offset additional (to affine and lla, vert_meters) x,y offset of
* the var_map (in output pixels). Used for spiral search for initial
......@@ -420,35 +374,13 @@ public class ComboMap {
* (is centered) to the reference image origin (vertical point)
*/
public static int [][] getPairOvelapBoundsAffine(
ComboMap ref_map,
ComboMap var_map,
OrthoMap ref_map,
OrthoMap var_map,
int zoom_lev,
int [] var_offset,
double [][][] gpu_affine){
return null;
}
/**
* Get rectified bounds of all provided combo images relative to the origin (vertical
* point) of the first one in pixels at the provided zoom level. Center of the first
* image correspond to the {0,0} point from which the bounds are counted
* @param maps array of map instances
* @param zoom_level zoom level - 0 corresponds to 1pix=1cm scale, +1 - to 1pix = 0.5cm
* @return {{min_x,max_x},{min_y, max_y}} relative to the origin of the first image
*/
public static int [][] getBoundsPixels(
ComboMap [] maps,
int zoom_level){ // maps[0] as a reference
double [][] bounds_meters = getBoundsMeters(maps);
double pix_size = getPixelSizeMeters (zoom_level);
int [][] bounds_pix = new int[2][2];
for (int n = 0; n < bounds_pix.length; n++) {
bounds_pix[n][0] = (int) Math.floor(bounds_meters[n][0]/pix_size);
bounds_pix[n][1] = (int) Math.ceil (bounds_meters[n][1]/pix_size);
}
return bounds_pix;
}
/**
* Convert from the affine matrix that calculates image coordinates from the rectified one
......@@ -550,14 +482,10 @@ public class ComboMap {
* Copy prepared scaled image to the top-left corner of the fixed-size
* float[] array for the GPU input
* @param zoom_level
* @param gpu_width
* @param gpu_height
* @return
*/
public float [] getPaddedGPU (
int zoom_level,
int gpu_width,
int gpu_height){
int zoom_level){
boolean got_image = downScaleForGPU (zoom_level); // will create if does not exist
if (!got_image) {
System.out.println("getPaddedGPU(): failed to prepare a scaled image");
......@@ -593,179 +521,7 @@ public class ComboMap {
return padded_gpu;
}
public static ImagePlus renderMulti (
String title,
boolean use_alt,
boolean show_centers,
ComboMap [] maps,
int zoom_level,
int [] origin){
int [] wh = new int[2];
double [][] centers = new double [maps.length][];
float [][] multi = renderMulti (
use_alt, // boolean use_alt,
maps, // ComboMap [] maps,
zoom_level, // int zoom_level,
wh, // int [] wh,
origin, // int [] origin){ // maps[0] as a reference
centers); // double [][] centers)
String [] map_names = new String[maps.length];
for (int n = 0; n < maps.length; n++) {
map_names[n] = maps[n].getName()+"_"+maps[n].getLocalDateTime().toString().replace("T","_")+"_UTC";
}
ImageStack stack = ShowDoubleFloatArrays.makeStack(
multi,
wh[0],
wh[1],
map_names,
false);
ImagePlus imp = new ImagePlus(title, stack);
if (show_centers) {
PointRoi roi = new PointRoi();
for (int i = 0; i < centers.length; i++) {
roi.addPoint(centers[i][0],centers[i][1], i+1);
}
roi.setOptions("label");
imp.setRoi(roi);
}
return imp;
}
/**
* Rectify and render multiple images (as slices) matching vert_meters to
* their (vert_meters points) provided IMS coordinates
* @param show altitudes instead of thermal data
* @param maps array of the combo images
* @param zoom_level zoom level (0 - 1pix=1cm, 1 - 1pix=0.5cm, -1 - 1pix=2cm
* @param wh null or int [2], will return {width, height}
* @param origin - null or double [2], will return {x0, y0} in pixels
* @param centers - null or double[maps.length][] - will return image centers coordinates
* @return
*/
public static float [][] renderMulti (
boolean use_alt,
ComboMap [] maps,
int zoom_level,
int [] wh,
int [] origin, // maps[0] as a reference
double [][] centers){
int [][] bounds = getBoundsPixels( // should be for rectified, {-bounds[0][0], -bounds[0][1]} - exact center
maps,
zoom_level);
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];
}
final float [][] fpixels = new float [maps.length][width * height];
for (int nmap = 0; nmap< maps.length; nmap++) {
final int fnmap = nmap;
Arrays.fill(fpixels[nmap], Float.NaN);
final double scale = 1.0/getPixelSizeMeters(zoom_level);
final double src_scale = 1.0/getPixelSizeMeters(maps[nmap].orig_zoom_level); // pix per meter
// metric bounds of the rectified image relative to its origin
double [][] mbounds = maps[nmap].getBoundsMeters(true);
double [] enu_offset = maps[0].enuOffsetTo(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[nmap] = 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
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=maps[nmap].getBoundsMeters (true);
final double [] src_center = {-src_bounds[0][0],-src_bounds[1][0]}; // x,y center offset in the source image
final double [][] affine = maps[nmap].affine;
final int src_width = use_alt? maps[nmap].getAltData().width: maps[nmap].getImageData().width;
final int src_height = use_alt? maps[nmap].getAltData().height : maps[nmap].getImageData().height;
final float [] src_img = use_alt? maps[nmap].getAltData().data : maps[nmap].getImageData().data;
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 ((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];
fpixels[fnmap][opX + opY*width] = (float) d;
}
}
}
}
};
}
ImageDtt.startAndJoin(threads);
}
return fpixels;
}
public static double [][] correlateComboPair(
CLTParameters clt_parameters,
ComboMap [] combo_maps,
int first_index,
int second_index,
int zoom_lev,
double [] offset_xy_second, // on top of affine and GPS
final int debugLevel){
int [] gpu_pair = {first_index,second_index};
double [][][] bounds_out = new double [2][][];
float [][] gpu_pair_img = new float [2][];
for (int n = 0; n < gpu_pair.length; n++) {
gpu_pair_img[n] = combo_maps[gpu_pair[n]].getPaddedGPU (
zoom_lev, // int zoom_level,
gpu_width, // int gpu_width,
gpu_height); // int gpu_height)
}
if (debugLevel > 1) {
String [] map_names = {combo_maps[gpu_pair[0]].getName(),combo_maps[gpu_pair[1]].getName()};
ShowDoubleFloatArrays.showArrays(
gpu_pair_img,
gpu_width,
gpu_height,
true,
"gpu_pair-zoom"+zoom_lev+"-"+combo_maps[gpu_pair[0]].getName()+"-"+combo_maps[gpu_pair[1]].getName(),
map_names);
}
return null;
}
public static void testVideo(ImagePlus imp) {
String path_prefix = "/media/elphel/SSD3-4GB/lwir16-proc/ortho_videos/videos/video_";
boolean keep_original = true;
......
package com.elphel.imagej.orthomosaic;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import com.elphel.imagej.cameras.CLTParameters;
import com.elphel.imagej.common.ShowDoubleFloatArrays;
import com.elphel.imagej.tileprocessor.ImageDtt;
import ij.ImagePlus;
import ij.ImageStack;
import ij.gui.PointRoi;
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"};
OrthoMap [] ortho_maps;
transient HashMap<Double,Integer> map_index;
transient HashMap<String,Integer> map_index_string;
private void writeObject(ObjectOutputStream oos) throws IOException { // ?
oos.defaultWriteObject();
// oos.writeObject(path);
// ortho_maps is not transient
}
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
ois.defaultReadObject();
reindex(); // will recreate map_index, map_index_string
// lla is not transient
}
public OrthoMapsCollection(
String path) {
if (path.endsWith(".list")) {
String[] paths = getPathsFromSorceList(path);
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++) {
ortho_maps[n] = new OrthoMap(paths[n]);
ortho_maps[n].setAffine(affine);
}
}
Arrays.sort(ortho_maps);
reindex();
}
public int reindex() {
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_string.put(ortho_maps[i].name,i);
}
return ortho_maps.length;
}
OrthoMap [] getMaps() {
return ortho_maps;
}
public String[] getNames() {
String [] names = new String [ortho_maps.length];
for (int n = 0; n < ortho_maps.length; n++) {
names[n] = ortho_maps[n].getName();
}
return names;
}
public static String[] getPathsFromSorceList(
String files_list) {
List<String> lines;
List<String> rel_files = new ArrayList<String>();
Path seq_path = Paths.get(files_list);
try {
lines = Files.readAllLines(seq_path, StandardCharsets.UTF_8);
// lines.stream().forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
return null;
}
Path base_path = seq_path.getParent();
HashMap<String,String> dir_map = new HashMap<String,String>();
for (String line:lines){
if (line.split("#").length > 0) {
String[] tokens = line.split("#")[0].trim().split("[\\s,;=]+");
if ((tokens.length > 2) && (tokens[0].toUpperCase().equals("SET"))) {
parse_set:
{
for (String dir_name:KEY_DIRS) if (dir_name.equals(tokens[1])) {
dir_map.put(dir_name,tokens[2]);
System.out.println("Parsed SET: "+tokens[1]+" in line: "+line);
break parse_set;
}
System.out.println("*********** Unknown SET: "+tokens[1]+" in line: "+line);
}
} else if ((tokens.length == 1) && (tokens[0].length() > 0)) {
rel_files.add(tokens[0]);
}
}
}
if (dir_map.containsKey("rootDirectory")) {
base_path=base_path.resolve(Paths.get(dir_map.get("rootDirectory")));
File base_dir = new File(base_path.toString());
}
String sourceDirectory = base_path.toString();
// set sourceDirectory:
if (dir_map.containsKey("sourceDirectory")) {
sourceDirectory=(base_path.resolve(Paths.get(dir_map.get("sourceDirectory")))).toString();
}
Path source_path = Paths.get(sourceDirectory);
// File source_dir = new File(source_path.toString());
String [] paths = new String[rel_files.size()];
for (int i = 0; i < paths.length; i++) {
paths[i] = (source_path.resolve(Paths.get(rel_files.get(i)))).toString();
}
return paths;
}
/**
* Get rectified bounds of all provided ortho images relative to the origin (vertical
* point) of the first one in pixels at the provided zoom level. Center of the first
* image correspond to the {0,0} point from which the bounds are counted
* @param zoom_level zoom level - 0 corresponds to 1pix=1cm scale, +1 - to 1pix = 0.5cm
* @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();
double pix_size = OrthoMap.getPixelSizeMeters (zoom_level);
int [][] bounds_pix = new int[2][2];
for (int n = 0; n < bounds_pix.length; n++) {
bounds_pix[n][0] = (int) Math.floor(bounds_meters[n][0]/pix_size);
bounds_pix[n][1] = (int) Math.ceil (bounds_meters[n][1]/pix_size);
}
return bounds_pix;
}
/**
* 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++) {
double [][] bounds_other = ortho_maps[nmap].getBoundsMeters(true);
double [] enuOffset = ortho_maps[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;
}
public ImagePlus renderMulti (
String title,
boolean use_alt,
boolean show_centers,
// OrthoMap [] maps,
int zoom_level,
int [] origin){
int [] wh = new int[2];
double [][] centers = new double [ortho_maps.length][];
float [][] multi = renderMulti (
use_alt, // boolean use_alt,
// maps, // OrthoMap [] maps,
zoom_level, // int zoom_level,
wh, // int [] wh,
origin, // int [] origin){ // maps[0] as a reference
centers); // double [][] centers)
String [] map_names = new String[ortho_maps.length];
for (int n = 0; n < ortho_maps.length; n++) {
map_names[n] = ortho_maps[n].getName()+"_"+ortho_maps[n].getLocalDateTime().toString().replace("T","_")+"_UTC";
}
ImageStack stack = ShowDoubleFloatArrays.makeStack(
multi,
wh[0],
wh[1],
map_names,
false);
ImagePlus imp = new ImagePlus(title, stack);
if (show_centers) {
PointRoi roi = new PointRoi();
for (int i = 0; i < centers.length; i++) {
roi.addPoint(centers[i][0],centers[i][1], i+1);
}
roi.setOptions("label");
imp.setRoi(roi);
}
return imp;
}
/**
* Rectify and render multiple images (as slices) matching vert_meters to
* their (vert_meters points) provided IMS coordinates
* @param show altitudes instead of thermal data
* @param maps array of the ortho images
* @param zoom_level zoom level (0 - 1pix=1cm, 1 - 1pix=0.5cm, -1 - 1pix=2cm
* @param wh null or int [2], will return {width, height}
* @param origin - null or double [2], will return {x0, y0} in pixels
* @param centers - null or double[maps.length][] - will return image centers coordinates
* @return
*/
public float [][] renderMulti (
boolean use_alt,
// OrthoMap [] maps,
int zoom_level,
int [] wh,
int [] origin, // maps[0] as a reference
double [][] centers){
int [][] bounds = getBoundsPixels( // should be for rectified, {-bounds[0][0], -bounds[0][1]} - exact center
zoom_level);
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];
}
final float [][] fpixels = new float [ortho_maps.length][width * height];
for (int nmap = 0; nmap< ortho_maps.length; nmap++) {
final int fnmap = nmap;
Arrays.fill(fpixels[nmap], 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);
double [] enu_offset = ortho_maps[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[nmap] = 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
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);
final double [] src_center = {-src_bounds[0][0],-src_bounds[1][0]}; // x,y center offset in the source image
final double [][] affine = ortho_maps[nmap].affine;
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;
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 ((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];
fpixels[fnmap][opX + opY*width] = (float) d;
}
}
}
}
};
}
ImageDtt.startAndJoin(threads);
}
return fpixels;
}
public float [][] getPaddedPairGPU(
String [] spair,
int zoom_lev){
int [] pair = new int [spair.length];
for (int i = 0; i < pair.length; i++) {
pair[i] = map_index_string.get(spair[i]);
}
return getPaddedPairGPU(
pair, // int [] pair,
zoom_lev); // int zoom_lev,
}
private float [][] getPaddedPairGPU(
int [] pair,
int zoom_lev){
float [][] gpu_pair_img = new float [2][];
for (int n = 0; n < pair.length; n++) {
gpu_pair_img[n] = ortho_maps[pair[n]].getPaddedGPU (
zoom_lev); // int zoom_level,
}
return gpu_pair_img;
}
public double [][] correlateOrthoPair(
CLTParameters clt_parameters,
String first_name,
String second_name,
int zoom_lev,
double [] offset_xy_second, // on top of affine and GPS
final int debugLevel){
int first_index = map_index_string.get(first_name);
int second_index = map_index_string.get(second_name);
return correlateOrthoPair(
clt_parameters, // CLTParameters clt_parameters,
first_index, // int first_index,
second_index, // int second_index,
zoom_lev, // int zoom_lev,
offset_xy_second, // double [] offset_xy_second, // on top of affine and GPS
debugLevel); // final int debugLevel)
}
private double [][] correlateOrthoPair(
CLTParameters clt_parameters,
int first_index,
int second_index,
int zoom_lev,
double [] offset_xy_second, // on top of affine and GPS
final int debugLevel){
int [] gpu_pair = {first_index,second_index};
double [][][] bounds_out = new double [2][][];
float [][] gpu_pair_img = new float [2][];
for (int n = 0; n < gpu_pair.length; n++) {
gpu_pair_img[n] = ortho_maps[gpu_pair[n]].getPaddedGPU (
zoom_lev); // int zoom_level,
}
if (debugLevel > 1) {
String [] map_names = {ortho_maps[gpu_pair[0]].getName(),ortho_maps[gpu_pair[1]].getName()};
ShowDoubleFloatArrays.showArrays(
gpu_pair_img,
OrthoMap.gpu_width,
OrthoMap.gpu_height,
true,
"gpu_pair-zoom"+zoom_lev+"-"+ortho_maps[gpu_pair[0]].getName()+"-"+ortho_maps[gpu_pair[1]].getName(),
map_names);
}
return null;
}
public static OrthoMapsCollection readOrthoMapsCollection (
String path) throws IOException, ClassNotFoundException {
FileInputStream fileInputStream = new FileInputStream(path);
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
OrthoMapsCollection orthoMapsCollection = (OrthoMapsCollection) objectInputStream.readObject();
objectInputStream.close();
return orthoMapsCollection;
}
public void writeOrthoMapsCollection (String path) throws IOException {
FileOutputStream fileOutputStream = new FileOutputStream(path);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(this);
objectOutputStream.flush();
objectOutputStream.close();
}
/*
float [][] gpu_pair_img = new float [2][];
for (int n = 0; n < gpu_pair.length; n++) {
gpu_pair_img[n] = ortho_maps[gpu_pair[n]].getPaddedGPU (
zoom_lev, // int zoom_level,
gpu_width, // int gpu_width,
gpu_height); // int gpu_height)
}
map_index_string
*/
}
package com.elphel.imagej.orthomosaic;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class PairwiseOrthoMatch implements Serializable {
public PairwiseOrthoMatch(double [][] affine, double [][] jtj) {
this.affine = affine;
this.jtj = jtj;
}
private static final long serialVersionUID = 1L;
public double [][] affine = new double[2][3];
public transient double [][] jtj = new double [6][6];
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject();
for (int i = 0; i < jtj.length; i++) {
for (int j = i; j < jtj[i].length; j++) {
oos.writeObject(jtj[i][j]);
}
}
}
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
ois.defaultReadObject();
for (int i = 0; i < jtj.length; i++) {
for (int j = i; j < jtj[i].length; j++) {
jtj[i][j] = (Double) ois.readObject();
if (j > i) {
jtj[j][i] = jtj[i][j];
}
}
}
}
//private void readObjectNoData() throws ObjectStreamException; // used to modify default values
}
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