Commit 2c606e97 authored by Andrey Filippov's avatar Andrey Filippov

Cleanup, improving photometry equalization

parent 603fc517
......@@ -366,11 +366,16 @@ public class CLTParameters {
public double fom_inf_range = 0.8; // 0.5
// Photometric calibration (move elsewhere)?
public boolean photo_en = false; // perform photogrammetric calibration to equalize pixel values
public boolean photo_en = false; // perform photogrammetric calibration to equalize pixel values and update config
public boolean photo_each = true; // perform photogrammetric calibration for each series ref scene and config
public boolean photo_to_main = true; // propagate scene sequence photometrics to the main instance to be applied
// to the next sequence and saved with corr-xml configuration. It is always
// propagated when calibration is run manually with photo_en = true
public int photo_num_full = 3; // Number of full recalibrations with re-processing of the images
public int photo_num_refines = 3; // Calibrate, remove outliers, recalibrate, ...
public double photo_min_strength = 0.0; // maybe add to filter out weak tiles
public double photo_max_diff = 40.0; // To filter mismatches. Normal (adjusted) have RMSE ~9
public int photo_order = 2; // Approximation order: 0 - just offset, 1 - linear, 2 - quadratic
public boolean photo_debug = false; // Generate images and text
......@@ -1355,10 +1360,13 @@ public class CLTParameters {
properties.setProperty(prefix+"fom_inf_range", this.fom_inf_range+"");
properties.setProperty(prefix+"photo_en", this.photo_en+""); // boolean
properties.setProperty(prefix+"photo_each", this.photo_each+""); // boolean
properties.setProperty(prefix+"photo_to_main", this.photo_to_main+""); // boolean
properties.setProperty(prefix+"photo_num_full", this.photo_num_full+""); // int
properties.setProperty(prefix+"photo_num_refines", this.photo_num_refines+""); // int
properties.setProperty(prefix+"photo_min_strength", this.photo_min_strength+""); // double
properties.setProperty(prefix+"photo_max_diff", this.photo_max_diff+""); // double
properties.setProperty(prefix+"photo_order", this.photo_order+""); // int
properties.setProperty(prefix+"photo_debug", this.photo_debug+""); // boolean
properties.setProperty(prefix+"show_textures", this.show_textures+"");
......@@ -2219,10 +2227,13 @@ public class CLTParameters {
if (properties.getProperty(prefix+"fom_inf_range")!=null) this.fom_inf_range=Double.parseDouble(properties.getProperty(prefix+"fom_inf_range"));
if (properties.getProperty(prefix+"photo_en")!=null) this.photo_en=Boolean.parseBoolean(properties.getProperty(prefix+"photo_en"));
if (properties.getProperty(prefix+"photo_each")!=null) this.photo_each=Boolean.parseBoolean(properties.getProperty(prefix+"photo_each"));
if (properties.getProperty(prefix+"photo_to_main")!=null) this.photo_to_main=Boolean.parseBoolean(properties.getProperty(prefix+"photo_to_main"));
if (properties.getProperty(prefix+"photo_num_full")!=null) this.photo_num_full=Integer.parseInt(properties.getProperty(prefix+"photo_num_full"));
if (properties.getProperty(prefix+"photo_num_refines")!=null) this.photo_num_refines=Integer.parseInt(properties.getProperty(prefix+"photo_num_refines"));
if (properties.getProperty(prefix+"photo_min_strength")!=null) this.photo_min_strength=Double.parseDouble(properties.getProperty(prefix+"photo_min_strength"));
if (properties.getProperty(prefix+"photo_max_diff")!=null) this.photo_max_diff=Double.parseDouble(properties.getProperty(prefix+"photo_max_diff"));
if (properties.getProperty(prefix+"photo_order")!=null) this.photo_order=Integer.parseInt(properties.getProperty(prefix+"photo_order"));
if (properties.getProperty(prefix+"photo_debug")!=null) this.photo_debug=Boolean.parseBoolean(properties.getProperty(prefix+"photo_debug"));
if (properties.getProperty(prefix+"show_textures")!=null) this.show_textures=Boolean.parseBoolean(properties.getProperty(prefix+"show_textures"));
......@@ -3218,6 +3229,11 @@ public class CLTParameters {
gd.addMessage ("It is applied when the source files are read.");
gd.addCheckbox ("Enable photometric calibration", this.photo_en,
"Equalize per- sensor gains and offsets. Requires disparity map. Save to reference scene and with current scene (to .corr-zml).");
gd.addCheckbox ("Re-calibrate photometric for each series", this.photo_each,
"Equalize per- sensor gains and offsets for each reference scene. Requires disparity map. Save to reference scene and with current scene (to .corr-xml).");
gd.addCheckbox ("Propagate per-series to main", this.photo_to_main,
"Propagate scene sequence photometrics to the main instance to be applied to the next sequence and saved with corr-xml configuration."+
"\nIt is always propagated when calibration is run manually with photo_en = true.");
gd.addNumericField("Full photometric (re)calibrations", this.photo_num_full, 0,3,"",
"Full recalibratrions include re-importing raw images with updated offsets/gains");
gd.addNumericField("Refines", this.photo_num_refines, 0,3,"",
......@@ -3226,7 +3242,10 @@ public class CLTParameters {
"Do not use weak tiles.");
gd.addNumericField("Maximal channel mismatch", this.photo_max_diff, 5,7,"",
"Detect (and remove outliers). Adjusted images have RMSE ~9 counts.");
gd.addCheckbox ("Debug pphotometric calibration", this.photo_debug,
gd.addNumericField("Approximation polynomial order", this.photo_order, 0,3,"",
"0 - only offset, 1 - linear, 2 - quadratic");
gd.addCheckbox ("Debug photometric calibration", this.photo_debug,
"Generate debug images an text output.");
......@@ -4225,10 +4244,13 @@ public class CLTParameters {
this.fom_inf_range= gd.getNextNumber();
this.photo_en = gd.getNextBoolean();
this.photo_each = gd.getNextBoolean();
this.photo_to_main = gd.getNextBoolean();
this.photo_num_full = (int) gd.getNextNumber();
this.photo_num_refines = (int) gd.getNextNumber();
this.photo_min_strength = gd.getNextNumber();
this.photo_max_diff = gd.getNextNumber();
this.photo_order = (int) gd.getNextNumber();
this.photo_debug = gd.getNextBoolean();
this.show_textures= gd.getNextBoolean();
......
......@@ -502,6 +502,35 @@ public class ImagejJp4Tiff {
return pixels;
}
public static boolean needsFix000E6410C435(ImagePlus imp) {
String prefix = "STD_";
String serial = (String) imp.getProperty(prefix+"Serial_Number");
if ((serial == null) || !serial.equals(FIXCH6_SERIAL)) {
return false; // wrong camera
}
String schannel = ((String) imp.getProperty(prefix+"PageNumber")).substring(0,1);
if ((schannel == null) || (Integer.parseInt(schannel) != FIXCH6_CHANNEL)) {
return false; // wrong channel
}
String sfdate = (String) imp.getProperty(prefix+"DateTime");
sfdate = sfdate.replaceFirst(":", "-");
sfdate = sfdate.replaceFirst(":", "-")+".000";
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS");
try {
Date startDate = dateFormat.parse(FIXCH6_EARLIEST);
Date endDate = dateFormat.parse(FIXCH6_LATEST);
Date fileDate = dateFormat.parse(sfdate);
if (fileDate.before(startDate) || fileDate.after(endDate)) {
return false; // too early or too late
}
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return true;
}
// fixing "00:0E:64:10:C4:35" camera
public int fix000E6410C435(
Hashtable<String, Object> meta_hash,
......
......@@ -1060,6 +1060,9 @@ public class CLTPass3d{
}
public int setTileOpDisparity(
double [] disparity) {
if (disparity== null) {
disparity=new double[tileProcessor.getTilesX() * tileProcessor.getTilesY()]; // all defined, infinity
}
boolean [] selection = new boolean [disparity.length];
for (int i = 0; i < disparity.length; i++) {
selection[i] = !Double.isNaN(disparity[i]);
......
......@@ -2435,7 +2435,7 @@ public class QuadCLT extends QuadCLTCPU {
scales_new[n] = scales_old[n]/scales[n];
offsets_new[n] = offsets_old[n] + offsets[n] / scales_old[n];
}
System.out.println("calibratePhotometric() Updated calibration:");
System.out.println("calibratePhotometric() Final calibration:");
for (int n = 0; n < num_sens; n++) {
System.out.println(String.format("%2d: %8.4f %8.6f %8.6f", n,offsets_new[n],scales_new[n], scales2_new[n]));
}
......@@ -2452,6 +2452,7 @@ public class QuadCLT extends QuadCLTCPU {
final QuadCLT ref_scene, // now - may be null - for testing if scene is rotated ref
final double min_strength,
final double max_diff, // 30.0
final int photo_order, // 0 - offset only, 1 - linear, 2 - quadratic
final int num_refines, // 2
final double [][] combo_dsn_final, // double [][] combo_dsn_final, // dls,
int threadsMax,
......@@ -2484,7 +2485,9 @@ public class QuadCLT extends QuadCLTCPU {
"PHOTOMETRIC", // String suffix,
threadsMax, // int threadsMax,
-2); // final int debugLevel);
img_ref.show();
if (debug) {
img_ref.show();
}
ImageStack imageStack = img_ref.getStack();
int num_sens=imageStack.getSize();
float [] fpixels;
......@@ -2496,23 +2499,11 @@ public class QuadCLT extends QuadCLTCPU {
dpixels[n][i] = fpixels[i];
}
}
boolean quadratic = true;
// double [][] dpix_orig = new double[num_sens][];
// for (int n = 0; n < num_sens; n++) {
// dpix_orig[n] = dpixels[n].clone();
// }
// boolean quadratic = true;
int width = img_ref.getWidth();
int height = img_ref.getHeight();
int len = width* height;
double [] avg_pix = new double [len];
double [] offsets = new double[dpixels.length];
double [] scales = new double[dpixels.length];
double [] scales2 = new double[dpixels.length];
double s0 = 0.0;
double sx= 0.0;
double sx2 = 0.0;
double [] sy = new double[num_sens];
double [] sxy = new double[num_sens];
boolean [] good_pix = new boolean[len];
double [][] pa_coeff = new double[num_sens][];
double min_abs_a = 1E-9;
......@@ -2539,7 +2530,7 @@ public class QuadCLT extends QuadCLTCPU {
num_good++;
}
}
double [][] pa_data = new double [num_good][2];
double [][][] pa_data = new double [num_sens][num_good][2];
double [][] raw = new double [num_sens][len];
for (int nsens = 0; nsens < num_sens; nsens++) {
Arrays.fill(raw[nsens],Double.NaN); // debug only
......@@ -2557,15 +2548,28 @@ public class QuadCLT extends QuadCLTCPU {
p= (-b + Math.sqrt(b*b - 4 * a * c))/(2 * a);
}
raw[nsens][i] = p;
pa_data[indx][0] = p; // dpixels[nsens][i];
pa_data[indx][1] = avg_pix[i];
pa_data[nsens][indx][0] = p; // dpixels[nsens][i];
pa_data[nsens][indx][1] = avg_pix[i];
indx++;
}
// quadratic
pa_coeff[nsens] =(new PolynomialApproximation(0)).polynomialApproximation1d(pa_data, quadratic ? 2 : 1);
double a = pa_coeff[nsens][2];
double b = pa_coeff[nsens][1];
double c = pa_coeff[nsens][0];
double c, b, a;
int poly_debug = 0;
if (photo_order < 1) { // zero out scales2, keep old scales (even if not normalized)
a = 0;
b = scales_old[nsens];
double s = 0.0;
for (int i = 0; i < num_good; i++) {
s += pa_data[nsens][i][0] - pa_data[nsens][i][1]/b;
}
c = -s/num_good*b;
} else {
// need to balance quadratic so their average is 0
// linear or quadratic
pa_coeff[nsens] =(new PolynomialApproximation(poly_debug)).polynomialApproximation1d(pa_data[nsens], photo_order);
c = pa_coeff[nsens][0];
b = pa_coeff[nsens][1];
a = (pa_coeff[nsens].length > 2) ? pa_coeff[nsens][2] : 0.0;
}
double A = a;
double C = -c/b;
if (Math.abs(a) >= min_abs_a) {
......@@ -2576,17 +2580,48 @@ public class QuadCLT extends QuadCLTCPU {
scales_new [nsens] = B;
offs_new[nsens] = C;
}
if (debug) {
System.out.println("calibratePhotometric() nref="+nref);
/*
System.out.println(String.format("%3s %8s %8s %8s",
"chn"," c "," b ", " 1e6*a "));
for (int n = 0; n < num_sens; n++) {
System.out.println(String.format("%2d: %8.4f %8.6f %8.6f", n,pa_coeff[n][0],pa_coeff[n][1], 1e6*pa_coeff[n][2]));
// Make scales_new to be (geometric) average 1.0 (except photo_order == 0), update pa_data to match
if (photo_order > 0) {
double scales_avg = 1.0;
for (int nsens = 0; nsens < num_sens; nsens++) {
scales_avg *= scales_new [nsens];
}
System.out.println();
*/
scales_avg = Math.pow(scales_avg, 1.0/num_sens);
for (int nsens = 0; nsens < num_sens; nsens++) {
scales_new [nsens] /= scales_avg;
scales2_new [nsens] /= scales_avg;
for (int i = 0; i < num_good; i++) {
pa_data[nsens][i][1] /= scales_avg;
}
}
}
// make scales2 be average zero, re-run scales
if (photo_order > 1) {
double scales2_offset = 0;
for (int nsens = 0; nsens < num_sens; nsens++) {
scales2_offset += scales2_new[nsens];
}
scales2_offset /= num_sens;
for (int nsens = 0; nsens < num_sens; nsens++) {
scales2_new[nsens] -= scales2_offset;
}
// modify pa_data, re-run linear
for (int nsens = 0; nsens < num_sens; nsens++) {
for (int i = 0; i < num_good; i++) {
double poffs = pa_data[nsens][i][0] - offs_new[nsens];
pa_data[nsens][i][1] -=poffs*poffs*scales2_new[nsens];
}
pa_coeff[nsens] =(new PolynomialApproximation(0)).polynomialApproximation1d(
pa_data[nsens], 1);
double c = pa_coeff[nsens][0];
double b = pa_coeff[nsens][1];
scales_new [nsens] = b;
offs_new[nsens] = -c/b;
}
}
if (true) { // debug) { during debug,
System.out.println("DEBUG: calibratePhotometric() nref="+nref);
System.out.println(String.format("%3s %10s %8s %8s %10s %8s %8s",
// "chn"," c "," b ", " 1e6*a "));
"chn"," offs0 ","scale0 ", "scale20"," offs "," scale ", " scale2"));
......@@ -2596,9 +2631,8 @@ public class QuadCLT extends QuadCLTCPU {
offs_new[n],scales_new[n], 1e6*scales2_new[n]));
}
System.out.println();
}
if (debug) {
double [][] diffs = new double [num_sens][len];
for (int n = 0; n < num_sens; n++) {
Arrays.fill(diffs[n], Double.NaN);
......@@ -2746,8 +2780,10 @@ public class QuadCLT extends QuadCLTCPU {
dbg_img[i][nTile] = pXpYD[nTile][i];
}
}
for (int i = 0; i <2; i++) {
dbg_img[3 + i][nTile] = mb_tau * mb_vectors[i][nTile];
if (mb_vectors!=null) {
for (int i = 0; i <2; i++) {
dbg_img[3 + i][nTile] = mb_tau * mb_vectors[i][nTile];
}
}
}
(new ShowDoubleFloatArrays()).showArrays( // out of boundary 15
......
......@@ -1253,10 +1253,11 @@ public class QuadCLTCPU {
if (!isLwir()) { // colorProcParameters.lwir_islwir) {
referenceExposures = eyesisCorrections.calcReferenceExposures(sourceFiles, debugLevel);
}
// move after restoring properties
/*
int [] channelFiles = set_channels[0].fileNumber();
boolean [][] saturation_imp = (clt_parameters.sat_level > 0.0)? new boolean[channelFiles.length][] : null;
double [] scaleExposures = new double[channelFiles.length];
// ImagePlus [] imp_srcs =
conditionImageSet(
clt_parameters, // EyesisCorrectionParameters.CLTParameters clt_parameters,
colorProcParameters, // ColorProcParameters colorProcParameters, //
......@@ -1277,6 +1278,7 @@ public class QuadCLTCPU {
threadsMax,
1); // debugLevel); // final int debug_level)
}
*/
// try to restore DSI generated from interscene if available, if not use single-scene -DSI_MAIN
int dsi_result = -1;
int max_length=OpticalFlow.COMBO_DSN_TITLES.length;
......@@ -1302,11 +1304,35 @@ public class QuadCLTCPU {
if (dsi_result < 0) {
System.out.println("No DSI data for the scene "+this.getImageName()+", setting this.dsi=null");
setDSI(null);
} else {
restoreInterProperties( // restore properties for interscene processing (extrinsics, ers, ...) // get relative poses (98)
null, // String path, // full name with extension or null to use x3d directory
false, // boolean all_properties,// null, // Properties properties, // if null - will only save extrinsics)
debugLevel);
}
// WAS: resore only if (dsi_result < 0)... else
restoreInterProperties( // restore properties for interscene processing (extrinsics, ers, ...) // get relative poses (98)
null, // String path, // full name with extension or null to use x3d directory
false, // boolean all_properties,// null, // Properties properties, // if null - will only save extrinsics)
debugLevel);
int [] channelFiles = set_channels[0].fileNumber();
boolean [][] saturation_imp = (clt_parameters.sat_level > 0.0)? new boolean[channelFiles.length][] : null;
double [] scaleExposures = new double[channelFiles.length];
conditionImageSet(
clt_parameters, // EyesisCorrectionParameters.CLTParameters clt_parameters,
colorProcParameters, // ColorProcParameters colorProcParameters, //
sourceFiles, // String [] sourceFiles,
this.image_name, // String set_name,
referenceExposures, // double [] referenceExposures,
channelFiles, // int [] channelFiles,
scaleExposures, // output // double [] scaleExposures
saturation_imp, // output // boolean [][] saturation_imp,
threadsMax, // int threadsMax,
debugLevelInner); // int debugLevel);
if (noise_sigma_level != null) {
generateAddNoise(
"-NOISE",
ref_scene, // final QuadCLTCPU ref_scene, // may be null if scale_fpn <= 0
noise_sigma_level,
noise_variant, //final int noise_variant, // <0 - no-variants, compatible with old code
threadsMax,
1); // debugLevel); // final int debug_level)
}
return this; // can only be QuadCLT instance
}
......@@ -1932,6 +1958,14 @@ public class QuadCLTCPU {
public void setPhotometricScene() {
setPhotometricScene(getImageName());
}
public boolean isPhotometricThis() {
if (this.photometric_scene == null) {
return false;
}
return this.photometric_scene.equals(getImageName());
}
public void setLwirScales(double [] scales) {
lwir_scales = scales; // will need to update properties!
......@@ -2253,7 +2287,7 @@ public class QuadCLTCPU {
for (int d = 0; d < fine_corr[n].length; d++){
for (int i = 0; i < fine_corr[n][d].length; i++){
String name = prefix+"fine_corr_"+n+fine_corr_dir_names[d]+fine_corr_coeff_names[i];
if (properties.getProperty(name)!=null) this.fine_corr[n][d][i]=Double.parseDouble(properties.getProperty(name));
if (properties.getProperty(name)!=null) this.fine_corr[n][d][i]=Double.parseDouble(properties.getProperty(name));// null
}
}
}
......@@ -5728,6 +5762,37 @@ public class QuadCLTCPU {
double [] offsets = getLwirOffsets();
double [] scales = getLwirScales();
double [] scales2 = getLwirScales2();
int needs_fix = -1;
for (int i = 0; i < imp_srcs.length; i++) {
if (ImagejJp4Tiff.needsFix000E6410C435(imp_srcs[i])) {
needs_fix = i;
break; // only one, and always 6
}
}
if (needs_fix >= 0) {
double [] fix_offsets = channelLwirEqualize( // now only calculates offsets, does not apply
channelFiles,
imp_srcs,
lwir_subtract_dc, // boolean remove_dc,
set_name, // just for debug messages == setNames.get(nSet)
threadsMax,
debugLevel);
double avg_offs = 0.0;
for (int i = 0; i < fix_offsets.length; i++) if (i != needs_fix) {
avg_offs += fix_offsets[i];
}
avg_offs /= (fix_offsets.length - 1);
int icorr = (int) Math.round((avg_offs - fix_offsets[needs_fix])/4096);
if (icorr != 0) {
float fcorr = icorr*4096;
System.out.println("Correcting "+imp_srcs[needs_fix].getTitle()+" by "+fcorr);
float [] pixels = (float []) imp_srcs[needs_fix].getProcessor().getPixels();
for (int i = 0; i < pixels.length; i++) {
pixels[i] += fcorr;
}
}
}
channelLwirApplyEqualize( // now apply (was part of channelLwirEqualize() )
channelFiles, // int [] channelFiles,
imp_srcs, // ImagePlus [] imp_srcs,
......@@ -6295,16 +6360,10 @@ public class QuadCLTCPU {
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() {
public void run() {
// for (int srcChannel=0; srcChannel < channelFiles.length; srcChannel++){
for (int srcChannel = ai.getAndIncrement(); srcChannel < channelFiles.length; srcChannel = ai.getAndIncrement()) {
int nFile=channelFiles[srcChannel];
if (nFile >=0) {
offsets[srcChannel]= avr_pix[srcChannel][0];
// float fd = (float)offsets[srcChannel];
// float [] pixels = (float []) imp_srcs[srcChannel].getProcessor().getPixels();
// for (int i = 0; i < pixels.length; i++) {
// pixels[i] -= fd;
// }
}
}
}
......
......@@ -84,6 +84,7 @@ public class TwoQuadCLT {
public static int DSI_SPREAD_AUX = 10;
public static int DSI_AVGVAL_MAIN = 11;
public static int DSI_AVGVAL_AUX = 12;
public static int DSI_LENGTH = DSI_AVGVAL_AUX+1;
public static String DSI_COMBO_SUFFIX = "-DSI_COMBO";
public static String DSI_MAIN_SUFFIX = "-DSI_MAIN";
......
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