Commit 86e73701 authored by Andrey Filippov's avatar Andrey Filippov

Trying row/col FPN correction

parent c3f2181a
package com.elphel.imagej.cuas;
import java.util.concurrent.atomic.AtomicInteger;
import com.elphel.imagej.common.ShowDoubleFloatArrays;
import com.elphel.imagej.tileprocessor.ImageDtt;
import com.elphel.imagej.tileprocessor.OpticalFlow;
import com.elphel.imagej.tileprocessor.QuadCLT;
import com.elphel.imagej.tileprocessor.QuadCLTCPU;
import ij.ImagePlus;
public class CorrectionFPN {
final QuadCLT quadCLT;
public CorrectionFPN(QuadCLTCPU quadCLT) {
this.quadCLT = (QuadCLT) quadCLT;
}
public void setApplyFPN(
QuadCLT [] quadCLTs,
double [][][] fpn) { // can be null to reset
quadCLT.setImageData(fpn);
quadCLT.setFPN(fpn);
for (int nscene = 0; nscene < quadCLTs.length; nscene++) if (quadCLTs[nscene] != null){
quadCLTs[nscene].setFPN(fpn);
quadCLTs[nscene].applyFPN();
}
return;
}
public void applyFPN(
double [][][] fpn) {
if (quadCLT.getImageFpnApplied() != fpn) {
if (quadCLT.getImageData() == null) {
System.out.println("applyFPN (): image_data==null");
return;
}
final double [][][] image_data = quadCLT.getImageData();
final double [][][] image_fpn_applied = quadCLT.getImageFpnApplied();
final Thread[] threads = ImageDtt.newThreadArray(ImageDtt.THREADS_MAX);
final AtomicInteger ai = new AtomicInteger(0);
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() {
public void run() {
for (int nChn = ai.getAndIncrement(); nChn < image_data.length; nChn = ai.getAndIncrement()) {
for (int ncol = 0; ncol < image_data[nChn].length; ncol++) {
for (int npix = 0; npix < image_data[nChn][ncol].length; npix++) {
if (image_fpn_applied != null) {
image_data[nChn][ncol][npix] += image_fpn_applied[nChn][ncol][npix];
}
if (fpn != null) {
image_data[nChn][ncol][npix] -= fpn[nChn][ncol][npix];
}
}
}
}
}
};
}
ImageDtt.startAndJoin(threads);
quadCLT.setFpnApplied(fpn);
quadCLT.setHasNewImageData(true);
}
return;
}
public ImagePlus saveShowFPN(
double [][][] fpn,
int width,
boolean save,
boolean show) {
ImagePlus imp = CorrectionFPN.showFPN(
fpn, // double [][][] fpn,
width, // int width,
show); // boolean show)
if (save && (imp != null)) {
quadCLT.saveImagePlusInModelDirectory(
QuadCLT.CENTER_FPN_SUFFIX, // String suffix, // null - use title from the imp
imp); // ImagePlus imp)
}
return imp;
}
public double [][][] readImageFPN (
int sens_mask){
int [] wh = new int [2];
double [][] fpn_data2 = quadCLT.readDoubleArrayFromModelDirectory(
QuadCLT.CENTER_FPN_SUFFIX, // String suffix,
0, // int num_slices, // (0 - all)
wh); // int [] wh)
if (fpn_data2 == null) {
return null;
}
int num_sensors = quadCLT.getNumSensors();
int used_sensors = 0;
for (int nsens = 0; nsens < num_sensors; nsens++) if ( (sens_mask & (1 << nsens)) != 0) {
used_sensors++;
}
int num_colors = fpn_data2.length/used_sensors;
double [][][] fpn = new double [num_sensors][num_colors][];
int isens = 0;
for (int nsens = 0; nsens < num_sensors; nsens++) if ( (sens_mask & (1 << nsens)) != 0) {
for (int ncol = 0; ncol < num_colors; ncol++) {
fpn[nsens][ncol] = fpn_data2[ncol * used_sensors + isens];
}
isens++;
}
return fpn;
}
public ImagePlus debugFPN(
QuadCLT [] quadCLTs,
double [][][] fpn,
int [] range,
int nsens,
double um_sigma,
boolean show) {
int fpn_width =quadCLT.getTilesX()*quadCLT.getTileSize(); // see if center_CLT can be used
String dbg_title = quadCLT.getImageName()+"-DEBUG_FPN_ROWCOL_SENS_"+nsens;
ImagePlus imp = debugFPN(
quadCLTs, // final QuadCLT [] quadCLTs,
fpn, // final double [][][] fpn,
range, // final int [] range, // required
nsens, // final int nsens,
fpn_width, // final int width,
um_sigma, // final double um_sigma,
1.0, // final double um_weight,
dbg_title); // final String title)
if (imp != null) {
quadCLT.saveImagePlusInModelDirectory(
dbg_title, // String suffix, // null - use title from the imp
imp); // ImagePlus imp)
if (show) {
imp.show();
}
}
return imp; // ************** restart from here!
}
public static ImagePlus showFPN(
double [][][] fpn,
int width,
boolean show) {
int num_sens = fpn.length;
int num_colors = -1; // fpn[0].length;
for (int i = 0; i < num_sens; i++) {
if (fpn[i] != null) {
num_colors = fpn[i].length;
break;
}
}
String [] titles = new String[num_sens];
String [] top_titles = new String[num_colors];
for (int ncol = 0; ncol < num_colors;ncol++) {
top_titles[ncol] = "Color-"+ncol;
}
double [][][] img_data = new double [num_colors][num_sens][];
for (int nsens = 0; nsens < num_sens; nsens++) if (fpn[nsens] != null) {
titles[nsens]= "Sens-"+nsens;
for (int ncol = 0; ncol < num_colors;ncol++) {
img_data [ncol][nsens] = fpn[nsens][ncol];
}
}
String fpn_title = "FPN_data";
ImagePlus imp = ShowDoubleFloatArrays.showArraysHyperstack(
img_data, // double[][][] pixels,
width, // int width,
fpn_title, // String title, "time_derivs-rt"+diff_time_rt+"-rxy"+diff_time_rxy,
titles, // String [] titles, // all slices*frames titles or just slice titles or null
top_titles, // String [] frame_titles, // frame titles or null
show); // boolean show)
return imp;
}
/**
* Calculate sensors FPN in a simple way average each channel/color for a scene sequence. Intended to be
* used in CUAS mode averaging several (normally 2 full periods of rotation.
* TODO: make more accurate and universal by back-propagating the corrected image and subtracting per-sensor
* versions before averaging.
* @param quadCLTs sequence of scenes with conditioned images (getImageData() != null), without FPN applied
* @param range a first_last scene index pair. May be adjusted to include an integer number of rotations.
* @param sensor_mask bitmask which sensors to process (normally -1 - all)
* @param debugLevel debug level
* @return [sensor][color][pixel] average FPN image. May be saved as image_data to a virtual (center) scene
* (a QuadCLT instance).
*/
public static double [][][] calculateFPN(
final QuadCLT [] quadCLTs,
final int [] range, // required
final int sensor_mask,
final int debugLevel){
QuadCLT first_scene = quadCLTs[range[0]];
final int num_sens = first_scene.getNumSensors();
final int num_colors = first_scene.getNumColors();
final int width = first_scene.getTilesX()*first_scene.getTileSize();
final int height = first_scene.getTilesY()*first_scene.getTileSize();
final int num_pix = width*height;
final int num_scenes = range[1]-range[0]+1;
final double [][][] fpn = new double [num_sens][num_colors][num_pix];
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 nChn = ai.getAndIncrement(); nChn < num_sens; nChn = ai.getAndIncrement()) if (((sensor_mask >> nChn) & 1) != 0) {
for (int nscene = range[0]; nscene <= range[1]; nscene++) {
for (int ncol = 0; ncol < num_colors; ncol++) {
double [] img_slice = quadCLTs[nscene].getImageData()[nChn][ncol];
for (int npix = 0; npix < num_pix; npix++) {
fpn[nChn][ncol][npix] += img_slice[npix];
}
}
}
double scale = 1.0/num_scenes;
for (int ncol = 0; ncol < num_colors; ncol++) {
for (int npix = 0; npix < num_pix; npix++) {
fpn[nChn][ncol][npix] *= scale;
}
}
}
}
};
}
ImageDtt.startAndJoin(threads);
return fpn;
}
public static int getImageHeight(
double [][][] image_data,
int width) {
for (int nChn = 0; nChn < image_data.length; nChn++) {
if (image_data[nChn] != null) {
return image_data[nChn][0].length/width;
}
}
return 0;
}
public static double [][] getRowAvg(
final double [][][] image_data,
final int width,
final double max_abs, // only average within +/- max_abs
final double weight_outlier){ // small weight of outliers to avoid instability
final int num_sens = image_data.length;
final int height = getImageHeight(
image_data, // final double [][][] image_data,
width); // final int width){
final double [][] image_row_avg = new double [num_sens][width];
final double [][] weight_row_avg = new double [num_sens][width];
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 nChn = ai.getAndIncrement(); nChn < num_sens; nChn = ai.getAndIncrement()) if (image_data[nChn] != null){
int indx = 0;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
double d = image_data[nChn][0][indx++];
double w = (Math.abs(d) <= max_abs) ? 1.0 : weight_outlier;
image_row_avg[nChn][x] += w*d;
weight_row_avg[nChn][x] += w;
}
}
for (int x = 0; x < width; x++) {
image_row_avg[nChn][x] /= weight_row_avg[nChn][x];
}
}
}
};
}
ImageDtt.startAndJoin(threads);
return image_row_avg;
}
public static double [][] getColAvg(
final double [][][] image_data,
final int width,
final double max_abs, // only average within +/- max_abs
final double weight_outlier){ // small weight of outliers to avoid instability
final int num_sens = image_data.length;
final int height = getImageHeight(
image_data, // final double [][][] image_data,
width); // final int width){
final double [][] image_col_avg = new double [num_sens][height];
final double [][] weight_col_avg = new double [num_sens][height];
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 nChn = ai.getAndIncrement(); nChn < num_sens; nChn = ai.getAndIncrement()) if (image_data[nChn] != null){
int indx = 0;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
double d = image_data[nChn][0][indx++];
double w = (Math.abs(d) <= max_abs) ? 1.0 : weight_outlier;
image_col_avg[nChn][y] += w*d;
weight_col_avg[nChn][y] += w;
}
}
for (int y = 0; y < height; y++) {
image_col_avg[nChn][y] /= weight_col_avg[nChn][y];
}
}
}
};
}
ImageDtt.startAndJoin(threads);
return image_col_avg;
}
public static double [][][] applyRowCol(
final double [][][] image_data,
final double [][] image_row_avg,
final double [][] image_col_avg,
final boolean inplace ){
final int num_sens = image_data.length;
final double [][][] out_data = new double [num_sens][1][];
int w=0,h=0;
for (int nchn = 0; nchn < num_sens; nchn ++) if (image_data[nchn] != null) {
out_data[nchn][0] = inplace ? image_data[nchn][0] : image_data[nchn][0].clone();
w = image_row_avg[nchn].length;
h = image_col_avg[nchn].length;
}
final int width = w;
final int height = h;
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 nChn = ai.getAndIncrement(); nChn < num_sens; nChn = ai.getAndIncrement()) if (image_data[nChn] != null){
int indx = 0;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
out_data[nChn][0][indx++] -= image_row_avg[nChn][x] + image_col_avg[nChn][y];
}
}
}
}
};
}
ImageDtt.startAndJoin(threads);
return out_data;
}
public static ImagePlus debugFPN(
final QuadCLT [] quadCLTs,
final double [][][] fpn,
final int [] range, // required
final int nsens,
final int width,
final double um_sigma,
final double um_weight,
final String title){
int ncol = 0;
int num_sens = quadCLTs[range[0]].getNumSensors();
double max_abs = 100.0;
double weight_outlier = 0.001;
int num_pix = quadCLTs[range[0]].getImageData()[nsens][ncol].length;
String [] titles_top = {"src","src-fpn","UM"+um_sigma, "row_col"};
double [][][] data = new double [titles_top.length][range[1]-range[0]+1][num_pix];
String [] titles = new String [range[1]-range[0]+1];
for (int nscene = range[0]; nscene <= range[1]; nscene++) {
int iscene = nscene - range[0];
titles [iscene] = quadCLTs[nscene].getImageName();
data[0][iscene] = quadCLTs[nscene].getImageData()[nsens][ncol].clone();
data[1][iscene] = data[0][iscene].clone();
for (int i = 0; i < num_pix; i++) {
data[1][iscene][i] -= fpn[nsens][ncol][i];
}
data[2][iscene] = data[1][iscene].clone();
OpticalFlow.applyUMDouble(
data[2][iscene], // final double [] data,
width, // final int width,
um_sigma, // final double um_sigma,
um_weight); // final double um_weight)
double [][][] data_chn = new double [num_sens][][];
data_chn[nsens] = new double [][] {data[2][iscene]};
double [][] image_row_avg = getRowAvg(
data_chn, // final double [][][] image_data,
width, // final int width,
max_abs, // final double max_abs, // only average within +/- max_abs
weight_outlier); // final double weight_outlier)
double [][] image_col_avg = getColAvg(
data_chn, // final double [][][] image_data,
width, // final int width,
max_abs, // final double max_abs, // only average within +/- max_abs
weight_outlier); // final double weight_outlier)
double [][][] data_out = applyRowCol(
data_chn, // final double [][][] image_data,
image_row_avg, // final double [][] image_row_avg,
image_col_avg, // final double [][] image_col_avg,
false); // final boolean inplace )
data[3][iscene] = data_out[nsens][0];
}
ImagePlus imp = ShowDoubleFloatArrays.showArraysHyperstack(
data, // double[][][] pixels,
width, // int width,
title, // String title, "time_derivs-rt"+diff_time_rt+"-rxy"+diff_time_rxy,
titles, // String [] titles, // all slices*frames titles or just slice titles or null
titles_top, // String [] frame_titles, // frame titles or null
false); // boolean show)
return imp;
}
}
...@@ -54,6 +54,7 @@ import com.elphel.imagej.common.DoubleGaussianBlur; ...@@ -54,6 +54,7 @@ import com.elphel.imagej.common.DoubleGaussianBlur;
import com.elphel.imagej.common.PolynomialApproximation; import com.elphel.imagej.common.PolynomialApproximation;
import com.elphel.imagej.common.ShowDoubleFloatArrays; import com.elphel.imagej.common.ShowDoubleFloatArrays;
import com.elphel.imagej.correction.CorrectionColorProc; import com.elphel.imagej.correction.CorrectionColorProc;
import com.elphel.imagej.cuas.CorrectionFPN;
import com.elphel.imagej.cuas.Cuas; import com.elphel.imagej.cuas.Cuas;
import com.elphel.imagej.cuas.CuasCenterLma; import com.elphel.imagej.cuas.CuasCenterLma;
import com.elphel.imagej.gpu.GPUTileProcessor; import com.elphel.imagej.gpu.GPUTileProcessor;
...@@ -5184,7 +5185,7 @@ public class OpticalFlow { ...@@ -5184,7 +5185,7 @@ public class OpticalFlow {
} }
} }
double [][][] fpn = center_CLT.readImageFPN ( -1); // int sens_mask); double [][][] fpn = center_CLT.getCorrectionFPN().readImageFPN ( -1); // int sens_mask);
if ((fpn == null) || cuas_calc_fpn) { if ((fpn == null) || cuas_calc_fpn) {
if (debugLevel >-3) { if (debugLevel >-3) {
System.out.println("Calculating FPN."); System.out.println("Calculating FPN.");
...@@ -5193,7 +5194,7 @@ public class OpticalFlow { ...@@ -5193,7 +5194,7 @@ public class OpticalFlow {
int rot_periods = (int) Math.floor(num_scenes/cuas_rot_period); int rot_periods = (int) Math.floor(num_scenes/cuas_rot_period);
int rot_scenes = (int) Math.floor(rot_periods *cuas_rot_period); int rot_scenes = (int) Math.floor(rot_periods *cuas_rot_period);
int [] rot_range = {0, rot_scenes-1}; int [] rot_range = {0, rot_scenes-1};
fpn = QuadCLT.calculateFPN( fpn = CorrectionFPN.calculateFPN(
quadCLTs, // final QuadCLT [] quadCLTs, quadCLTs, // final QuadCLT [] quadCLTs,
rot_range, // final int [] range, // required rot_range, // final int [] range, // required
-1, // final int sensor_mask, -1, // final int sensor_mask,
...@@ -5201,11 +5202,13 @@ public class OpticalFlow { ...@@ -5201,11 +5202,13 @@ public class OpticalFlow {
int dbg_sens = 12; int dbg_sens = 12;
if (cuas_debug && (dbg_sens >= 0)) { if (cuas_debug && (dbg_sens >= 0)) {
center_CLT.debugFPN( double um_sigma_fpn = 10.0;
center_CLT.getCorrectionFPN().debugFPN(
quadCLTs, // QuadCLT [] quadCLTs, quadCLTs, // QuadCLT [] quadCLTs,
fpn, // double [][][] fpn, fpn, // double [][][] fpn,
rot_range, // int [] range, rot_range, // int [] range,
dbg_sens, // int nsens, dbg_sens, // int nsens,
um_sigma_fpn, // double um_sigma = 10.0;
show_fpn); // boolean show) { show_fpn); // boolean show) {
} }
...@@ -5215,12 +5218,12 @@ public class OpticalFlow { ...@@ -5215,12 +5218,12 @@ public class OpticalFlow {
} }
} }
// center_CLT.setImageData(fpn); // included in center_CLT.setApplyFPN(). // setting FPN images to the virtual (center) scene // center_CLT.setImageData(fpn); // included in center_CLT.setApplyFPN(). // setting FPN images to the virtual (center) scene
center_CLT.saveShowFPN( center_CLT.getCorrectionFPN().saveShowFPN(
fpn,// double [][][] fpn, fpn,// double [][][] fpn,
fpn_width, // int width, fpn_width, // int width,
true, // boolean save, true, // boolean save,
show_fpn); // boolean show) { show_fpn); // boolean show) {
center_CLT.setApplyFPN( center_CLT.getCorrectionFPN().setApplyFPN(
quadCLTs, // QuadCLT [] quadCLTs, quadCLTs, // QuadCLT [] quadCLTs,
fpn);// double [][][] fpn) fpn);// double [][][] fpn)
} else { } else {
...@@ -5228,7 +5231,7 @@ public class OpticalFlow { ...@@ -5228,7 +5231,7 @@ public class OpticalFlow {
System.out.println("Skipping FPN."); System.out.println("Skipping FPN.");
} }
// center_CLT.setImageData(null); // center_CLT.setImageData(null);
center_CLT.setApplyFPN( center_CLT.getCorrectionFPN().setApplyFPN(
quadCLTs, // QuadCLT [] quadCLTs, quadCLTs, // QuadCLT [] quadCLTs,
null);// double [][][] fpn) null);// double [][][] fpn)
} }
...@@ -8621,8 +8624,27 @@ public class OpticalFlow { ...@@ -8621,8 +8624,27 @@ public class OpticalFlow {
imp_scenes.getProcessor().resetMinAndMax(); imp_scenes.getProcessor().resetMinAndMax();
return imp_scenes; return imp_scenes;
} }
public static void applyUMDouble(
final double [] data,
final int width,
final double um_sigma,
final double um_weight) {
double [] blurred = data.clone();
(new DoubleGaussianBlur()).blurDouble(
blurred, // double[] pixels,
width, // int width,
blurred.length/width, // int height,
um_sigma, // double sigmaX,
um_sigma, // double sigmaY,
0.01); // double accuracy)
for (int i = 0; i<data.length; i++) {
data[i] -= blurred[i]*um_weight;
}
}
public static double [][] getSceneSZXY( public static double [][] getSceneSZXY(
QuadCLT scene, QuadCLT scene,
......
...@@ -67,6 +67,7 @@ import com.elphel.imagej.common.ShowDoubleFloatArrays; ...@@ -67,6 +67,7 @@ import com.elphel.imagej.common.ShowDoubleFloatArrays;
import com.elphel.imagej.correction.CorrectionColorProc; import com.elphel.imagej.correction.CorrectionColorProc;
import com.elphel.imagej.correction.EyesisCorrections; import com.elphel.imagej.correction.EyesisCorrections;
import com.elphel.imagej.correction.Eyesis_Correction; import com.elphel.imagej.correction.Eyesis_Correction;
import com.elphel.imagej.cuas.CorrectionFPN;
import com.elphel.imagej.cuas.Cuas; import com.elphel.imagej.cuas.Cuas;
import com.elphel.imagej.cuas.CuasData; import com.elphel.imagej.cuas.CuasData;
import com.elphel.imagej.gpu.GpuQuad; import com.elphel.imagej.gpu.GpuQuad;
...@@ -171,7 +172,9 @@ public class QuadCLTCPU { ...@@ -171,7 +172,9 @@ public class QuadCLTCPU {
public double [][][] image_fpn = null; // [channel][color][pixel] Shared by images in a series, subtracted during conditioning public double [][][] image_fpn = null; // [channel][color][pixel] Shared by images in a series, subtracted during conditioning
// and applied to image_data; // and applied to image_data;
public double [][][] image_fpn_applied = null; // [channel][color][pixel] // when applying different instance, the old will be unapplied public double [][][] image_fpn_applied = null; // [channel][color][pixel] // when applying different instance, the old will be unapplied
boolean new_image_data = false; public double [][] image_row_avg = null; //[nsens][width] integrated by columns, lwir-only (single-color)
public double [][] image_col_avg = null; //[nsens][height] integrated by rows, lwir-only (single-color)
boolean new_image_data = false;
boolean [][] saturation_imp = null; // (near) saturated pixels or null boolean [][] saturation_imp = null; // (near) saturated pixels or null
boolean is_aux = false; boolean is_aux = false;
String photometric_scene = null; String photometric_scene = null;
...@@ -228,6 +231,19 @@ public class QuadCLTCPU { ...@@ -228,6 +231,19 @@ public class QuadCLTCPU {
public boolean center_is_parent = false; // when true - use image_path that discards current version public boolean center_is_parent = false; // when true - use image_path that discards current version
public String center_parent_dir = null; public String center_parent_dir = null;
public ImagePlus imp_center_average = null; public ImagePlus imp_center_average = null;
public CorrectionFPN correctionFPN = null;
public void setCorrectionFPN() {
correctionFPN = new CorrectionFPN(this);
}
public CorrectionFPN getCorrectionFPN() {
if (correctionFPN == null) {
correctionFPN = new CorrectionFPN(this);
}
return correctionFPN;
}
public ImagePlus getCenterAverage() { public ImagePlus getCenterAverage() {
...@@ -6458,6 +6474,11 @@ public class QuadCLTCPU { ...@@ -6458,6 +6474,11 @@ public class QuadCLTCPU {
public boolean hasNewImageData() { public boolean hasNewImageData() {
return new_image_data; return new_image_data;
} }
public void setHasNewImageData(boolean has_data) {
new_image_data = has_data;
}
public double [][][] getResetImageData(){ public double [][][] getResetImageData(){
new_image_data = false; new_image_data = false;
return image_data; return image_data;
...@@ -6466,6 +6487,10 @@ public class QuadCLTCPU { ...@@ -6466,6 +6487,10 @@ public class QuadCLTCPU {
return image_data; return image_data;
} }
public double [][][] getImageFpnApplied(){
return image_fpn_applied;
}
public void setImageData(double [][][] data) { public void setImageData(double [][][] data) {
image_data = data; image_data = data;
new_image_data = true; new_image_data = true;
...@@ -6473,6 +6498,10 @@ public class QuadCLTCPU { ...@@ -6473,6 +6498,10 @@ public class QuadCLTCPU {
image_fpn = null; image_fpn = null;
} }
public void setFpnApplied(double [][][] data) {
image_fpn_applied = data;
}
// magic scale should be set before using TileProcessor (calculated disparities depend on it) // magic scale should be set before using TileProcessor (calculated disparities depend on it)
public boolean isAux() {return is_aux;} // USED in lwir public boolean isAux() {return is_aux;} // USED in lwir
...@@ -9891,261 +9920,6 @@ public class QuadCLTCPU { ...@@ -9891,261 +9920,6 @@ public class QuadCLTCPU {
this.image_fpn = fpn; this.image_fpn = fpn;
} }
public void setApplyFPN(
QuadCLT [] quadCLTs,
double [][][] fpn) { // can be null to reset
setImageData(fpn);
setFPN(fpn);
for (int nscene = 0; nscene < quadCLTs.length; nscene++) if (quadCLTs[nscene] != null){
quadCLTs[nscene].setFPN(fpn);
quadCLTs[nscene].applyFPN();
}
return;
}
public void applyFPN() {
applyFPN(this.image_fpn);
}
public void applyFPN(
double [][][] fpn) {
if (image_fpn_applied != fpn) {
if (image_data == null) {
System.out.println("applyFPN (): image_data==null");
return;
}
final Thread[] threads = ImageDtt.newThreadArray(ImageDtt.THREADS_MAX);
final AtomicInteger ai = new AtomicInteger(0);
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() {
public void run() {
for (int nChn = ai.getAndIncrement(); nChn < image_data.length; nChn = ai.getAndIncrement()) {
for (int ncol = 0; ncol < image_data[nChn].length; ncol++) {
for (int npix = 0; npix < image_data[nChn][ncol].length; npix++) {
if (image_fpn_applied != null) {
image_data[nChn][ncol][npix] += image_fpn_applied[nChn][ncol][npix];
}
if (fpn != null) {
image_data[nChn][ncol][npix] -= fpn[nChn][ncol][npix];
}
}
}
}
}
};
}
ImageDtt.startAndJoin(threads);
image_fpn_applied = fpn;
this.new_image_data = true;
}
return;
}
public ImagePlus saveShowFPN(
double [][][] fpn,
int width,
boolean save,
boolean show) {
ImagePlus imp = showFPN(
fpn, // double [][][] fpn,
width, // int width,
show); // boolean show)
if (save && (imp != null)) {
saveImagePlusInModelDirectory(
CENTER_FPN_SUFFIX, // String suffix, // null - use title from the imp
imp); // ImagePlus imp)
}
return imp;
}
public static ImagePlus showFPN(
double [][][] fpn,
int width,
boolean show) {
int num_sens = fpn.length;
int num_colors = -1; // fpn[0].length;
for (int i = 0; i < num_sens; i++) {
if (fpn[i] != null) {
num_colors = fpn[i].length;
break;
}
}
String [] titles = new String[num_sens];
String [] top_titles = new String[num_colors];
for (int ncol = 0; ncol < num_colors;ncol++) {
top_titles[ncol] = "Color-"+ncol;
}
double [][][] img_data = new double [num_colors][num_sens][];
for (int nsens = 0; nsens < num_sens; nsens++) if (fpn[nsens] != null) {
titles[nsens]= "Sens-"+nsens;
for (int ncol = 0; ncol < num_colors;ncol++) {
img_data [ncol][nsens] = fpn[nsens][ncol];
}
}
String fpn_title = "FPN_data";
ImagePlus imp = ShowDoubleFloatArrays.showArraysHyperstack(
img_data, // double[][][] pixels,
width, // int width,
fpn_title, // String title, "time_derivs-rt"+diff_time_rt+"-rxy"+diff_time_rxy,
titles, // String [] titles, // all slices*frames titles or just slice titles or null
top_titles, // String [] frame_titles, // frame titles or null
show); // boolean show)
return imp;
}
public double [][][] readImageFPN (
int sens_mask){
int [] wh = new int [2];
double [][] fpn_data2 = readDoubleArrayFromModelDirectory(
CENTER_FPN_SUFFIX, // String suffix,
0, // int num_slices, // (0 - all)
wh); // int [] wh)
if (fpn_data2 == null) {
return null;
}
int num_sensors = getNumSensors();
int used_sensors = 0;
for (int nsens = 0; nsens < num_sensors; nsens++) if ( (sens_mask & (1 << nsens)) != 0) {
used_sensors++;
}
int num_colors = fpn_data2.length/used_sensors;
double [][][] fpn = new double [num_sensors][num_colors][];
int isens = 0;
for (int nsens = 0; nsens < num_sensors; nsens++) if ( (sens_mask & (1 << nsens)) != 0) {
for (int ncol = 0; ncol < num_colors; ncol++) {
fpn[nsens][ncol] = fpn_data2[ncol * used_sensors + isens];
}
isens++;
}
return fpn;
}
/**
* Calculate sensors FPN in a simple way average each channel/color for a scene sequence. Intended to be
* used in CUAS mode averaging several (normally 2 full periods of rotation.
* TODO: make more accurate and universal by back-propagating the corrected image and subtracting per-sensor
* versions before averaging.
* @param quadCLTs sequence of scenes with conditioned images (getImageData() != null), without FPN applied
* @param range a first_last scene index pair. May be adjusted to include an integer number of rotations.
* @param sensor_mask bitmask which sensors to process (normally -1 - all)
* @param debugLevel debug level
* @return [sensor][color][pixel] average FPN image. May be saved as image_data to a virtual (center) scene
* (a QuadCLT instance).
*/
public static double [][][] calculateFPN(
final QuadCLT [] quadCLTs,
final int [] range, // required
final int sensor_mask,
final int debugLevel){
QuadCLT first_scene = quadCLTs[range[0]];
final int num_sens = first_scene.getNumSensors();
final int num_colors = first_scene.getNumColors();
final int width = first_scene.getTilesX()*first_scene.getTileSize();
final int height = first_scene.getTilesY()*first_scene.getTileSize();
final int num_pix = width*height;
final int num_scenes = range[1]-range[0]+1;
final double [][][] fpn = new double [num_sens][num_colors][num_pix];
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 nChn = ai.getAndIncrement(); nChn < num_sens; nChn = ai.getAndIncrement()) if (((sensor_mask >> nChn) & 1) != 0) {
for (int nscene = range[0]; nscene <= range[1]; nscene++) {
for (int ncol = 0; ncol < num_colors; ncol++) {
double [] img_slice = quadCLTs[nscene].getImageData()[nChn][ncol];
for (int npix = 0; npix < num_pix; npix++) {
fpn[nChn][ncol][npix] += img_slice[npix];
}
}
}
double scale = 1.0/num_scenes;
for (int ncol = 0; ncol < num_colors; ncol++) {
for (int npix = 0; npix < num_pix; npix++) {
fpn[nChn][ncol][npix] *= scale;
}
}
}
}
};
}
ImageDtt.startAndJoin(threads);
return fpn;
}
public ImagePlus debugFPN(
QuadCLT [] quadCLTs,
double [][][] fpn,
int [] range,
int nsens,
boolean show) {
int fpn_width =getTilesX()*getTileSize(); // see if center_CLT can be used
String dbg_title = getImageName()+"-DEBUG_FPN_SENS_"+nsens;
ImagePlus imp=QuadCLT.debugFPN(
quadCLTs, // final QuadCLT [] quadCLTs,
fpn, // final double [][][] fpn,
range, // final int [] range, // required
nsens, // final int nsens,
fpn_width, // final int width,
dbg_title); // final String title)
if (imp != null) {
saveImagePlusInModelDirectory(
dbg_title, // String suffix, // null - use title from the imp
imp); // ImagePlus imp)
if (show) {
imp.show();
}
}
return imp;
}
public static ImagePlus debugFPN(
final QuadCLT [] quadCLTs,
final double [][][] fpn,
final int [] range, // required
final int nsens,
final int width,
final String title){
int ncol = 0;
int num_pix = quadCLTs[range[0]].getImageData()[nsens][ncol].length;
double [][][] data = new double [2][range[1]-range[0]+1][num_pix];
String [] titles_top = {"src","src-fpn"};
String [] titles = new String [range[1]-range[0]+1];
for (int nscene = range[0]; nscene <= range[1]; nscene++) {
int iscene = nscene - range[0];
titles [iscene] = quadCLTs[nscene].getImageName();
data[0][iscene] = quadCLTs[nscene].getImageData()[nsens][ncol].clone();
data[1][iscene] = data[0][iscene].clone();
for (int i = 0; i < num_pix; i++) {
data[1][iscene][i] -= fpn[nsens][ncol][i];
}
}
ImagePlus imp = ShowDoubleFloatArrays.showArraysHyperstack(
data, // double[][][] pixels,
width, // int width,
title, // String title, "time_derivs-rt"+diff_time_rt+"-rxy"+diff_time_rxy,
titles, // String [] titles, // all slices*frames titles or just slice titles or null
titles_top, // String [] frame_titles, // frame titles or null
false); // boolean show)
return imp;
}
public void processCLTQuadCorrs( // not used in lwir public void processCLTQuadCorrs( // not used in lwir
CLTParameters clt_parameters, CLTParameters clt_parameters,
...@@ -10228,6 +10002,9 @@ public class QuadCLTCPU { ...@@ -10228,6 +10002,9 @@ public class QuadCLTCPU {
IJ.d2s(0.000000001*(System.nanoTime()-this.startTime),3)+" sec, --- Free memory11="+Runtime.getRuntime().freeMemory()+" (of "+Runtime.getRuntime().totalMemory()+")"); IJ.d2s(0.000000001*(System.nanoTime()-this.startTime),3)+" sec, --- Free memory11="+Runtime.getRuntime().freeMemory()+" (of "+Runtime.getRuntime().totalMemory()+")");
} }
public void applyFPN() {
getCorrectionFPN().applyFPN(this.image_fpn);
}
public void processCLTQuadCorrsTestERS( public void processCLTQuadCorrsTestERS(
......
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