Commit 8dad66d7 authored by Andrey Filippov's avatar Andrey Filippov

histogram normalization, color combined textures

parent 3a520ab0
......@@ -392,6 +392,19 @@ public class CLTParameters {
public double tex_distort = 0.8; // Maximal texture distortion to accumulate multiple scenes (0 - any)
public double tex_mb = 1.0; // Reduce texture weight if motion blur exceeds this (as square of MB length)
public boolean tex_um = true; // Use unsharp mask filter for textures
public double tex_um_sigma = 10.0; // Unsharp mask sigma for textures
public double tex_um_weight = 0.97;// Unsharp mask weight
public boolean tex_lwir_autorange = true; // Autorange LWIR textures
public boolean tex_um_fixed = false; // Use fixed range after unsharp mask instead of autorange
public double tex_um_range = 500; // Full range after unsharp mask
public boolean tex_hist_norm = true; // Normalize texture histogram
public double tex_hist_amount = 0.7; // Texture histogram normalization amount (0.0 - no normalization, 1.0 - full normalization)
public int tex_hist_bins = 1024; // Number of histogram bins to use for texture histograms
public int tex_hist_segments = 32; // Number of evenly-spaced percentiles to use for histogram normalization
public boolean tex_color = true; // Use pseudo-colored textures
public int tex_palette = 2; // Palette number for pseudo colors
public boolean show_textures = true; // show generated textures
public boolean debug_filters = false;// show intermediate results of filtering
// not used anywhere so far
......@@ -1394,6 +1407,19 @@ public class CLTParameters {
properties.setProperty(prefix+"tex_distort", this.tex_distort+""); // double
properties.setProperty(prefix+"tex_mb", this.tex_mb+""); // double
properties.setProperty(prefix+"tex_um", this.tex_um+""); // boolean
properties.setProperty(prefix+"tex_um_sigma", this.tex_um_sigma+""); // double
properties.setProperty(prefix+"tex_um_weight", this.tex_um_weight+""); // double
properties.setProperty(prefix+"tex_lwir_autorange", this.tex_lwir_autorange+""); // boolean
properties.setProperty(prefix+"tex_um_fixed", this.tex_um_fixed+""); // boolean
properties.setProperty(prefix+"tex_um_range", this.tex_um_range+""); // double
properties.setProperty(prefix+"tex_hist_norm", this.tex_hist_norm+""); // boolean
properties.setProperty(prefix+"tex_hist_amount", this.tex_hist_amount+""); // double
properties.setProperty(prefix+"tex_hist_bins", this.tex_hist_bins+""); // int
properties.setProperty(prefix+"tex_hist_segments", this.tex_hist_segments+""); // int
properties.setProperty(prefix+"tex_color", this.tex_color+""); // boolean
properties.setProperty(prefix+"tex_palette", this.tex_palette+""); // int
properties.setProperty(prefix+"show_textures", this.show_textures+"");
properties.setProperty(prefix+"debug_filters", this.debug_filters+"");
......@@ -2274,6 +2300,19 @@ public class CLTParameters {
if (properties.getProperty(prefix+"tex_distort")!=null) this.tex_distort=Double.parseDouble(properties.getProperty(prefix+"tex_distort"));
if (properties.getProperty(prefix+"tex_mb")!=null) this.tex_mb=Double.parseDouble(properties.getProperty(prefix+"tex_mb"));
if (properties.getProperty(prefix+"tex_um")!=null) this.tex_um=Boolean.parseBoolean(properties.getProperty(prefix+"tex_um"));
if (properties.getProperty(prefix+"tex_um_sigma")!=null) this.tex_um_sigma=Double.parseDouble(properties.getProperty(prefix+"tex_um_sigma"));
if (properties.getProperty(prefix+"tex_um_weight")!=null) this.tex_um_weight=Double.parseDouble(properties.getProperty(prefix+"tex_um_weight"));
if (properties.getProperty(prefix+"tex_lwir_autorange")!=null) this.tex_lwir_autorange=Boolean.parseBoolean(properties.getProperty(prefix+"tex_lwir_autorange"));
if (properties.getProperty(prefix+"tex_um_fixed")!=null) this.tex_um_fixed=Boolean.parseBoolean(properties.getProperty(prefix+"tex_um_fixed"));
if (properties.getProperty(prefix+"tex_um_range")!=null) this.tex_um_range=Double.parseDouble(properties.getProperty(prefix+"tex_um_range"));
if (properties.getProperty(prefix+"tex_hist_norm")!=null) this.tex_hist_norm=Boolean.parseBoolean(properties.getProperty(prefix+"tex_hist_norm"));
if (properties.getProperty(prefix+"tex_hist_amount")!=null) this.tex_hist_amount=Double.parseDouble(properties.getProperty(prefix+"tex_hist_amount"));
if (properties.getProperty(prefix+"tex_hist_bins")!=null) this.tex_hist_bins=Integer.parseInt(properties.getProperty(prefix+"tex_hist_bins"));
if (properties.getProperty(prefix+"tex_hist_segments")!=null) this.tex_hist_segments=Integer.parseInt(properties.getProperty(prefix+"tex_hist_segments"));
if (properties.getProperty(prefix+"tex_color")!=null) this.tex_color=Boolean.parseBoolean(properties.getProperty(prefix+"tex_color"));
if (properties.getProperty(prefix+"tex_palette")!=null) this.tex_palette=Integer.parseInt(properties.getProperty(prefix+"tex_palette"));
if (properties.getProperty(prefix+"show_textures")!=null) this.show_textures=Boolean.parseBoolean(properties.getProperty(prefix+"show_textures"));
if (properties.getProperty(prefix+"debug_filters")!=null) this.debug_filters=Boolean.parseBoolean(properties.getProperty(prefix+"debug_filters"));
......@@ -3313,6 +3352,32 @@ public class CLTParameters {
"Maximal texture distortion to accumulate multiple scenes (neighbor tile center offset from the uniform grid. 0 - do not filter");
gd.addNumericField("Maximal motion blur to reduce weight",this.tex_mb, 5,7,"pix",
"Reduce texture weight if motion blur exceeds this (as square of MB length).");
gd.addMessage ("Textures rendering");
gd.addCheckbox ("Apply unsharp mask", this.tex_um,
"Use unsharp mask filter for texture.");
gd.addNumericField("Unsharp mask sigma", this.tex_um_sigma, 5,7,"pix",
"Unsharp mask sigma for textures.");
gd.addNumericField("Unsharp mask weight", this.tex_um_weight, 5,7,"",
"Unsharp mask weight.");
gd.addCheckbox ("Autorange LWIR textures", this.tex_lwir_autorange,
"Autorange LWIR textures (inluding after unsharp mask).");
gd.addCheckbox ("Fixed range (after unsharp only)", this.tex_um_fixed,
"Use fixed range after unsharp mask instead of autorange.");
gd.addNumericField("Full range", this.tex_um_range, 5,7,"counts",
"Full range after unsharp mask.");
gd.addCheckbox ("Normalize texture histogram", this.tex_hist_norm,
"Normalize texture histogram.");
gd.addNumericField("Histogram normalization amount", this.tex_hist_amount, 5,7,"",
"Texture histogram normalization amount (0.0 - no normalization, 1.0 - full normalization).");
gd.addNumericField("Number of histogram bins", this.tex_hist_bins, 0,3,"",
"Number of histogram bins to use for texture histograms.");
gd.addNumericField("Number of histogram percentiles", this.tex_hist_segments, 0,3,"",
"Number of evenly-spaced percentiles to use for histogram normalization.");
gd.addCheckbox ("Use pseudo-color", this.tex_color,
"Use pseudo-colored textures (false - monochrome, float).");
gd.addNumericField("Palette number", this.tex_palette, 0,3,"",
"Palette number for pseudo colors.");
gd.addMessage ("Earlier 3D generation parameters");
gd.addCheckbox ("Show generated textures", this.show_textures);
gd.addCheckbox ("show intermediate results of filtering", this.debug_filters);
......@@ -4329,6 +4394,19 @@ public class CLTParameters {
this.tex_distort = gd.getNextNumber();
this.tex_mb = gd.getNextNumber();
this.tex_um = gd.getNextBoolean();
this.tex_um_sigma = gd.getNextNumber();
this.tex_um_weight = gd.getNextNumber();
this.tex_lwir_autorange = gd.getNextBoolean();
this.tex_um_fixed = gd.getNextBoolean();
this.tex_um_range = gd.getNextNumber();
this.tex_hist_norm = gd.getNextBoolean();
this.tex_hist_amount = gd.getNextNumber();
this.tex_hist_bins = (int) gd.getNextNumber();
this.tex_hist_segments = (int) gd.getNextNumber();
this.tex_color = gd.getNextBoolean();
this.tex_palette = (int) gd.getNextNumber();
this.show_textures= gd.getNextBoolean();
this.debug_filters= gd.getNextBoolean();
this.min_smth= gd.getNextNumber();
......
......@@ -5296,10 +5296,12 @@ public class OpticalFlow {
clt_parameters, // CLTParameters clt_parameters,
colorProcParameters, // ColorProcParameters colorProcParameters,
rgbParameters, // EyesisCorrectionParameters.RGBParameters rgbParameters,
quadCLTs[quadCLTs.length-1], // quadCLT_main, // final QuadCLT parameter_scene, // to use for rendering parameters in multi-series sequences
// if null - use reference scene
quadCLTs, // QuadCLT [] scenes,
combo_dsn_final, //double [][] combo_dsn_final, // null OK, will read file
updateStatus, // final boolean updateStatus,
debugLevel + 1); // final int debugLevel)
debugLevel); // + 1); // final int debugLevel)
System.out.println ("TexturedModel.output3d() -> "+ok_3d);
}
......
......@@ -7292,21 +7292,21 @@ public class QuadCLTCPU {
return hist;
}
public static int [] getLwirHistogramSliceAlpha(
public static double [] getLwirHistogramSliceAlpha(
double [][] data,
double hard_cold,
double hard_hot,
int num_bins) {
int chn_y = 0;
int chn_alpha = 1;
int [] hist = new int [num_bins];
double [] hist = new double [num_bins];
double k = num_bins / (hard_hot - hard_cold);
for (int i = 0; i < data[chn_y].length; i++) {
double d = data[chn_y][i] * data[chn_alpha][i];
double d = data[chn_y][i] ;
int bin = (int) ((d - hard_cold)*k);
if (bin < 0) bin = 0;
else if (bin >= num_bins) bin = (num_bins -1);
hist[bin]++;
hist[bin] += data[chn_alpha][i];
}
return hist;
}
......@@ -7336,11 +7336,22 @@ public class QuadCLTCPU {
}
return this_hist;
}
public static double [] addHist( // USED in lwir
double [] this_hist,
double [] other_hist) {
for (int i = 0; i < this_hist.length; i++) {
this_hist[i] += other_hist[i];
}
return this_hist;
}
// get low/high (soft min/max) from the histogram
// returns value between 0.0 (low histogram limit and 1.0 - high histgram limit
public static double getMarginFromHist( // USED in lwir
int [] hist, // histogram
double cumul_val, // cummulative number of items to be ignored
double cumul_val, // cumulative number of items to be ignored
boolean high_marg) { // false - find low margin(output ~0.0) , true - find high margin (output ~1.0)
int n = 0;
int n_prev = 0;
......@@ -7374,6 +7385,44 @@ public class QuadCLTCPU {
return v;
}
public static double getMarginFromHist( // USED in lwir
double [] hist, // histogram
double cumul_val, // cumulative number of items to be ignored
boolean high_marg) { // false - find low margin(output ~0.0) , true - find high margin (output ~1.0)
double n = 0;
double n_prev = 0;
int bin;
double s = 1.0 / hist.length;
double v;
if (high_marg) {
for (bin = hist.length -1; bin >= 0; bin--) {
n_prev = n;
n+= hist[bin];
if (n > cumul_val) break;
}
if (n <= cumul_val) { // not used in lwir
v = 0.0; // cumul_val > total number of samples
} else {
v = s* (bin + 1 - (cumul_val - n_prev)/(n - n_prev));
}
} else {
for (bin = 0; bin < hist.length; bin++) {
n_prev = n;
n+= hist[bin];
if (n > cumul_val) break;
}
if (n <= cumul_val) { // not used in lwir
v = 1.0; // cumul_val > total number of samples
} else {
v = s * (bin + (cumul_val - n_prev)/(n - n_prev));
}
}
return v;
}
public static double [] autorange( // USED in lwir (double input)
double [][][] iclt_data, // [iQuad][ncol][i] - normally only [][2][] is non-null
double hard_cold,// matches data, DC (this.lwir_offset) subtracted
......@@ -7485,13 +7534,9 @@ public class QuadCLTCPU {
double too_cold, // pixels per image
double too_hot, // pixels per image
int num_bins) {
// int num_channels = textures[0].length;
// boolean is_mono = num_channels < 3;
// too_cold *= num_chn; // iclt_data.length;
// too_hot *= num_chn; // iclt_data.length;
int [] hist = null;
double [] hist = null;
for (int nslice = 0; nslice < textures.length; nslice++) {
int [] this_hist = getLwirHistogramSliceAlpha(
double [] this_hist = getLwirHistogramSliceAlpha(
textures[nslice], // double [][][] data,
hard_cold,
hard_hot,
......@@ -7520,7 +7565,236 @@ public class QuadCLTCPU {
return abs_lim;
}
public static double [] getMinMaxTextures(
double [][][] textures // [nslices][nchn][i]
) {
if ((textures == null) || (textures.length==0)) {
throw new IllegalArgumentException ("umTextures(): Empty textures");
}
final boolean is_mono = textures[0].length <= 2;
final int alpha_chn = is_mono? 1 : 3;
final boolean has_alpha = textures[0].length > alpha_chn;
final double alpha_min = 0.9; // only consider pixels with higher alpha
final int num_um_chn = alpha_chn * textures.length; // number of images to UM
final Thread[] threads = ImageDtt.newThreadArray(QuadCLT.THREADS_MAX);
final AtomicInteger ai = new AtomicInteger(0);
final AtomicInteger ati = new AtomicInteger(0);
final double [][] tminmax = new double [threads.length][];
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() {
public void run() {
int thread_num = ati.getAndIncrement();
tminmax[thread_num] = new double [] {Double.NaN,Double.NaN};
for (int nimg = ai.getAndIncrement(); nimg < num_um_chn; nimg = ai.getAndIncrement()) {
int nslice = nimg / alpha_chn;
int nchn = nimg % alpha_chn;
double [] alpha = has_alpha ? textures[nslice][alpha_chn] : null;
double [] texture = textures[nslice][nchn];
for (int i = 0; i < texture.length; i++) if (!has_alpha || (alpha[i] > alpha_min)) {
if (!(texture[i] >= tminmax[thread_num][0])) {
tminmax[thread_num][0] = texture[i];
}
if (!(texture[i] <= tminmax[thread_num][1])) {
tminmax[thread_num][1] = texture[i];
}
}
}
}
};
}
ImageDtt.startAndJoin(threads);
double [] minmax = {Double.NaN,Double.NaN};
for (int i = 0; i < tminmax.length; i++) if ((tminmax[i] != null) && !Double.isNaN(tminmax[i][0]) &&!Double.isNaN(tminmax[i][1])) {
if (!(tminmax[i][0] >= minmax[0])) {
minmax[0] = tminmax[i][0];
}
if (!(tminmax[i][1] <= minmax[1])) {
minmax[1] = tminmax[i][1];
}
}
return minmax;
}
public static void umTextures(
final double [][][] textures, // [nslices][nchn][i]
final int width,
final double um_sigma,
final double um_weight){
if ((textures == null) || (textures.length==0)) {
throw new IllegalArgumentException ("umTextures(): Empty textures");
}
final int height = textures[0][0].length / width;
final boolean is_mono = textures[0].length <= 2;
final int alpha_chn = is_mono? 1 : 3;
final boolean has_alpha = textures[0].length > alpha_chn;
final int num_um_chn = alpha_chn * textures.length; // number of images to UM
final Thread[] threads = ImageDtt.newThreadArray(QuadCLT.THREADS_MAX);
final AtomicInteger ai = new AtomicInteger(0);
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() {
public void run() {
double [] texture_orig = new double [width * height];
for (int nimg = ai.getAndIncrement(); nimg < num_um_chn; nimg = ai.getAndIncrement()) {
int nslice = nimg / alpha_chn;
int nchn = nimg % alpha_chn;
double [] texture = textures[nslice][nchn];
double [] texture_alpha = has_alpha? textures[nslice][alpha_chn]:null;
System.arraycopy(texture, 0, texture_orig,0,texture.length);
(new DoubleGaussianBlur()).blurDouble(
texture, // FloatProcessor ip,
width,
height,
um_sigma, // double sigmaX,
um_sigma, // double sigmaY,
0.01); // double accuracy)
for (int i = 0; i < texture.length; i++) {
texture[i] = texture_orig[i] - um_weight * texture[i];
}
if (has_alpha) {
for (int i = 0; i < texture.length; i++) if (texture_alpha[i] <= 0.0){
texture[i] = 0.0;
}
}
}
}
};
}
ImageDtt.startAndJoin(threads);
}
public static double [] getHistogramNormalization(
double [][][] textures, // [nslices][nchn][i]
double [] minmax,
int num_bins,
int num_nodes,
double hist_normalize_amount // 1.0 - full
) {
double [] hist = null;
for (int nslice = 0; nslice < textures.length; nslice++) {
double [] this_hist = getLwirHistogramSliceAlpha(
textures[nslice], // double [][][] data,
minmax[0],
minmax[1],
num_bins);
if (hist == null) {
hist = this_hist;
} else {
addHist(
hist,
this_hist);
}
}
for (int i = 1; i < hist.length; i++) {
hist[i] += hist[i - 1];
}
if (hist_normalize_amount < 1.0) {
double full = hist[hist.length-1];
for (int i = 0; i < hist.length; i++) {
hist[i] = hist_normalize_amount * hist[i] + (1.0-hist_normalize_amount) * full;
}
}
double [] norm_table = new double [num_nodes];
int indx = 0;
// norm_table[num_nodes - 1] = 1.0;
for (int n = 0; n < num_nodes; n++) {
double thresh = hist[hist.length - 1] * (n + 1) /(num_nodes + 1);
if (thresh > hist[hist.length - 1]) { // full histogram
thresh = hist[hist.length - 1];
}
for (; ((indx < hist.length) && (hist[indx] <= thresh)); indx++);
double hist_prev = (indx == 0) ? 0.0: hist[indx-1];
if (indx >= hist.length) {
norm_table[n] = 1.0; // not normal?
} else {
norm_table[n] = (1.0 * indx)/hist.length +
(thresh - hist_prev)/(hist[indx] - hist_prev)/hist.length;
}
}
return norm_table;
}
/**
* Calculate long inverted table from short normalization one for fast application
* @param direct_table
* @param num_bins
* @return
*/
public static double [] invertHistogramNormalization(
double [] direct_table, // last is <1.0, first > 0
int num_bins) {
int num_nodes = direct_table.length;
double out_scale = num_bins - 1; // 1023, last is 1.0
double out_step = 1.0 / out_scale;
double [] inverted_table = new double [num_bins];
int i_last = -1;
double inv_step = 1.0/(direct_table.length+1);
for (int indx = 0; indx <= direct_table.length; indx++) {
double frac_l = (indx > 0) ? direct_table[indx-1] : 0.0;
double frac_h = (indx == direct_table.length) ? 1.0 : direct_table[indx];
int i_l = (int) Math.floor(frac_l*out_scale);
int i_h = (int) Math.ceil (frac_h*out_scale);
if (i_h > num_bins) i_h = num_bins;
if (i_l <= i_last) i_l++;
i_last = i_h - 1;
double out_range = frac_h - frac_l;
if (out_range <= 0) {
continue; // may happen at the beginning/end?
}
double inv_l = inv_step * indx;
for (int i = i_l; i < i_h; i++) {
double out_diff = i * out_step - frac_l;
inverted_table[i] = inv_l + out_diff * inv_step / out_range;
}
}
inverted_table[inverted_table.length-1] = 1.0;
return inverted_table;
}
public static void applyTexturesNormHist(
final double [][][] textures, // [nslices][nchn][i]
final double [] min_max,
final double [] inv_table)
{
final double range = min_max[1] - min_max[0];
final boolean is_mono = textures[0].length <= 2;
final int alpha_chn = is_mono? 1 : 3;
final boolean has_alpha = textures[0].length > alpha_chn;
final Thread[] threads = ImageDtt.newThreadArray(QuadCLT.THREADS_MAX);
final AtomicInteger ai = new AtomicInteger(0);
for (int nslice = 0; nslice < textures.length; nslice++) {
final double [] texture_alpha = (has_alpha) ? textures[nslice][alpha_chn] : null;
for (int nchn = 0; nchn < alpha_chn; nchn ++) {
final double [] texture = textures[nslice][nchn];
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() {
public void run() {
for (int npix = ai.getAndIncrement(); npix < texture.length; npix = ai.getAndIncrement()) {
if (!has_alpha || (texture_alpha[npix] > 0.0)) {
double rel_in = (texture[npix] - min_max[0]) / range;
if (rel_in < 0.0) {
rel_in = 0.0;
} else if (rel_in > 1.0) {
rel_in = 1.0;
}
// interpolate by table
double srel_in = rel_in * inv_table.length;
int il = (int) Math.floor(srel_in);
if (il > (inv_table.length-1)) il = inv_table.length -1;
double dl = inv_table[il];
double dh = (il < (inv_table.length-1)) ? inv_table[il+1] : 1.0;
texture[npix] = min_max[0]+ (dl + (srel_in - il) * (dh-dl))*range;
}
}
}
};
}
ImageDtt.startAndJoin(threads);
ai.set(0);
}
}
return;
}
// float, for GPU
public ImagePlus linearStackToColor( // not used in lwir
CLTParameters clt_parameters,
......@@ -7540,30 +7814,50 @@ public class QuadCLTCPU {
)
{
// convert to ImageStack of 3 slices
String [] sliceNames = {"red", "blue", "green"};
int green_index = 2;
float [][] rbg_in;
String [] sliceNames = isMonochrome()? new String[]{"mono"}: new String[]{"red", "blue", "green"};
int main_color_index = isMonochrome()? 0 : 2;
// int green_index = 2;
float [][] rbg_in = isMonochrome()?
new float [][] {iclt_data[0]} :
new float[][] {iclt_data[0],iclt_data[1],iclt_data[2]};
/*
if (iclt_data.length >= 3) {
rbg_in = new float [][] {iclt_data[0],iclt_data[1],iclt_data[2]}; // RBG or LWIR CPU
} else {
rbg_in = new float [][] {iclt_data[0],iclt_data[0],iclt_data[0]}; // after LWIR/GPU
green_index = 0;
}
float [] alpha = null; // (0..1.0)
if (iclt_data.length > 3) alpha = iclt_data[3];
*/
float [] alpha_pixels = null; // (0..1.0)
if (iclt_data.length > rbg_in.length) {
alpha_pixels = iclt_data[rbg_in.length];
}
if (isLwir()) {
//// if (!colorProcParameters.lwir_pseudocolor) {
if (!toRGB) {
ImageProcessor ip= new FloatProcessor(width,height);
ip.setPixels(iclt_data[0]);
ip.resetMinAndMax();
ImagePlus imp = new ImagePlus(name+suffix, ip);
if (!toRGB) { // Double [][] uses
/// if (!colorProcParameters.lwir_pseudocolor) {
ImagePlus imp;
if (alpha_pixels != null) {
String [] titles = {"Y","alpha"};
ImageStack stack = ShowDoubleFloatArrays.makeStack(
new float[][] {rbg_in[0],alpha_pixels}, // iclt_data,
width, // (tilesX + 0) * clt_parameters.transform_size,
height, // (tilesY + 0) * clt_parameters.transform_size,
titles, // or use null to get chn-nn slice names
true); // replace NaN with 0.0
imp = new ImagePlus(name+suffix, stack);
imp.getProcessor().resetMinAndMax();
} else {
ImageProcessor ip= new FloatProcessor(width,height);
ip.setPixels(rbg_in[0]);
ip.resetMinAndMax();
imp = new ImagePlus(name+suffix, ip);
}
return imp;
}
String [] rgb_titles = {"red","green","blue"};
String [] rgba_titles = {"red","green","blue","alpha"};
String [] titles = (alpha == null) ? rgb_titles : rgba_titles;
int num_slices = (alpha == null) ? 3 : 4;
String [] titles = (alpha_pixels == null) ? rgb_titles : rgba_titles;
int num_slices = (alpha_pixels == null) ? 3 : 4;
double mn = colorProcParameters.lwir_low;
double mx = colorProcParameters.lwir_high;
double [] cold_hot = getColdHot();
......@@ -7584,18 +7878,18 @@ public class QuadCLTCPU {
mx,
255.0);
float [][] rgba = new float [num_slices][];
for (int i = 0; i < 3; i++) rgba[i] = new float [iclt_data[green_index].length];
for (int i = 0; i < rbg_in[green_index].length; i++) {
for (int i = 0; i < 3; i++) rgba[i] = new float [iclt_data[main_color_index].length];
for (int i = 0; i < rbg_in[main_color_index].length; i++) {
// if (i == 700) {
// System.out.println("linearStackToColor(): i="+i);
// }
float [] rgb = tc.getRGB(iclt_data[green_index][i]);
float [] rgb = tc.getRGB(iclt_data[main_color_index][i]);
rgba[0][i] = rgb[0]; // red
rgba[1][i] = rgb[1]; // green
rgba[2][i] = rgb[2]; // blue
}
if (alpha != null) {
rgba[3] = alpha; // 0..1
if (alpha_pixels != null) {
rgba[3] = alpha_pixels; // 0..1
}
ImageStack stack = ShowDoubleFloatArrays.makeStack(
rgba, // iclt_data,
......@@ -7636,7 +7930,7 @@ public class QuadCLTCPU {
saveShowIntermediate, // boolean saveShowIntermediate, // save/show if set globally
saveShowFinal, // boolean saveShowFinal, // save/show result (color image?)
stack, // ImageStack stack,
alpha, // float [] alpha_pixels,
alpha_pixels, // float [] alpha_pixels,
width, // int width, // int tilesX,
height, // int height, // int tilesY,
scaleExposure, // double scaleExposure,
......@@ -7685,21 +7979,23 @@ public class QuadCLTCPU {
for (int i = 0; i < pixels.length; i++) {
pixels[i] = (float) iclt_data[0][i];
}
ImagePlus imp;
if (alpha_pixels != null) {
/*
String [] titles = {"Y","alpha"};
ImageStack stack = ShowDoubleFloatArrays.makeStack(
{pixels,alpha_pixels}, // iclt_data,
new float[][] {pixels,alpha_pixels}, // iclt_data,
width, // (tilesX + 0) * clt_parameters.transform_size,
height, // (tilesY + 0) * clt_parameters.transform_size,
titles, // or use null to get chn-nn slice names
true); // replace NaN with 0.0
*/
imp = new ImagePlus(name+suffix, stack);
imp.getProcessor().resetMinAndMax();
} else {
ImageProcessor ip= new FloatProcessor(width,height);
ip.setPixels(pixels);
ip.resetMinAndMax();
imp = new ImagePlus(name+suffix, ip);
}
ImageProcessor ip= new FloatProcessor(width,height);
ip.setPixels(pixels);
ip.resetMinAndMax();
ImagePlus imp = new ImagePlus(name+suffix, ip);
return imp;
}
String [] rgb_titles = {"red","green","blue"};
......@@ -7783,7 +8079,107 @@ public class QuadCLTCPU {
debugLevel); //int debugLevel
}
// float, for GPU
public static ImagePlus linearStackToColorLWIR(
CLTParameters clt_parameters,
int lwir_palette, // <0 - do not convert
double [] minmax,
String name,
String suffix, // such as disparity=...
boolean toRGB,
double [][] texture_data,
int width, // int tilesX,
int height, // int tilesY,
int debugLevel )
{
final boolean is_mono = texture_data.length <= 2;
final int alpha_chn = is_mono? 1 : 3;
// final boolean has_alpha = texture_data.length > alpha_chn;
// convert to ImageStack of 3 slices
// String [] sliceNames = is_mono? new String[]{"mono"}: new String[]{"red", "blue", "green"};
int main_color_index = is_mono? 0 : 2;
double [][] rbg_in = is_mono?
new double [][] {texture_data[0]} :
new double[][] {texture_data[0],texture_data[1],texture_data[2]};
double [] alpha_pixels = null; // (0..1.0)
if (texture_data.length > rbg_in.length) {
alpha_pixels = texture_data[rbg_in.length];
}
if (!toRGB) { // Double [][] uses
ImagePlus imp;
if (alpha_pixels != null) {
String [] titles = {"Y","alpha"};
ImageStack stack = ShowDoubleFloatArrays.makeStack(
new double[][] {rbg_in[0],alpha_pixels}, // iclt_data,
width, // (tilesX + 0) * clt_parameters.transform_size,
height, // (tilesY + 0) * clt_parameters.transform_size,
titles, // or use null to get chn-nn slice names
true); // replace NaN with 0.0
imp = new ImagePlus(name+suffix, stack);
imp.getProcessor().resetMinAndMax();
} else {
ImageProcessor ip= new FloatProcessor(width,height);
ip.setPixels(rbg_in[0]);
ip.resetMinAndMax();
imp = new ImagePlus(name+suffix, ip);
}
return imp;
}
String [] rgb_titles = {"red","green","blue"};
String [] rgba_titles = {"red","green","blue","alpha"};
String [] titles = (alpha_pixels == null) ? rgb_titles : rgba_titles;
int num_slices = (alpha_pixels == null) ? 3 : 4;
double mn = minmax[0]; // colorProcParameters.lwir_low;
double mx = minmax[1]; // colorProcParameters.lwir_high;
ThermalColor tc = new ThermalColor(
lwir_palette, // colorProcParameters.lwir_palette,
mn,
mx,
255.0);
double [][] rgba = new double [num_slices][];
for (int i = 0; i < 3; i++) rgba[i] = new double [texture_data[main_color_index].length];
for (int i = 0; i < rbg_in[main_color_index].length; i++) {
double [] rgb = tc.getRGB(texture_data[main_color_index][i]);
rgba[0][i] = rgb[0]; // red
rgba[1][i] = rgb[1]; // green
rgba[2][i] = rgb[2]; // blue
}
if (alpha_pixels != null) {
rgba[3] = alpha_pixels; // 0..1
}
ImageStack stack = ShowDoubleFloatArrays.makeStack(
rgba, // iclt_data,
width, // (tilesX + 0) * clt_parameters.transform_size,
height, // (tilesY + 0) * clt_parameters.transform_size,
titles, // or use null to get chn-nn slice names
true); // replace NaN with 0.0
ImagePlus imp_rgba = EyesisCorrections.convertRGBAFloatToRGBA32(
stack, // ImageStack stackFloat, //r,g,b,a
// name+"ARGB"+suffix, // String title,
name+suffix, // String title,
0.0, // double r_min,
255.0, // double r_max,
0.0, // double g_min,
255.0, // double g_max,
0.0, // double b_min,
255.0, // double b_max,
0.0, // double alpha_min,
1.0); // double alpha_max)
return imp_rgba;
}
// Convert a single value pixels to color (r,b,g) values to be processed instead of the normal colors
......
......@@ -32,6 +32,7 @@ import com.elphel.imagej.cameras.CLTParameters;
import com.elphel.imagej.cameras.ColorProcParameters;
import com.elphel.imagej.cameras.EyesisCorrectionParameters;
import com.elphel.imagej.common.ShowDoubleFloatArrays;
import com.elphel.imagej.correction.EyesisCorrections;
import com.elphel.imagej.gpu.GpuQuad;
import com.elphel.imagej.gpu.TpTask;
import com.elphel.imagej.x3d.export.WavefrontExport;
......@@ -528,6 +529,8 @@ public class TexturedModel {
CLTParameters clt_parameters,
ColorProcParameters colorProcParameters,
EyesisCorrectionParameters.RGBParameters rgbParameters,
final QuadCLT parameter_scene, // to use for rendering parameters in multi-series sequences
// if null - use reference scene
QuadCLT [] scenes,
double [][] combo_dsn_final, // null OK, will read file
// final int threadsMax, // maximal number of threads to launch
......@@ -615,21 +618,45 @@ public class TexturedModel {
double [][] inter_weights = new double [tilesY][tilesX]; // per-tile texture weights for inter-scene accumulation;
double [][][][] inter_textures= new double [tilesY][tilesX][][]; // [channel][256] - non-overlapping textures
boolean [] scenes_sel = new boolean[scenes.length];
// for (int i = scenes.length - 10; i < scenes.length; i++) { // start with just one (reference) scene
// for (int i = scenes.length - 10; i < scenes.length; i++) { // start with just one (reference) scene
for (int i = 0; i < scenes.length; i++) { // start with just one (reference) scene
scenes_sel[i] = true;
}
boolean renormalize = true;// false - use normalizations from previous scenes to keep consistent colors
ImagePlus[] combined_textures = getInterCombinedTextures( // return ImagePlus[] matching tileClusters[], with alpha
clt_parameters, // final CLTParameters clt_parameters,
colorProcParameters, // ColorProcParameters colorProcParameters,
rgbParameters, // EyesisCorrectionParameters.RGBParameters rgbParameters,
parameter_scene, // final QuadCLT parameter_scene, // to use for rendering parameters in multi-series sequences
// if null - use reference scene
scenes, // final QuadCLT [] scenes,
scenes_sel, // final boolean [] scenes_sel, // null or which scenes to process
null, // final boolean [] selection, // may be null, if not null do not process unselected tiles
tileClusters, // final TileCluster [] tileClusters, // disparities, borders, selections for texture passes
// final int margin,
renormalize, // final boolean renormalize, // false - use normalizations from previous scenes to keep consistent colors
debugLevel); // final int debug_level)
boolean save_full_textures = true;
if (save_full_textures) {
EyesisCorrectionParameters.CorrectionParameters correctionsParameters = ref_scene.correctionsParameters;
for (int nslice = 0; nslice < combined_textures.length; nslice++) {
String path= correctionsParameters.selectX3dDirectory(
//TODO: Which one to use - name or this.image_name ?
correctionsParameters.getModelName(ref_scene.getImageName()), // quad timestamp. Will be ignored if correctionsParameters.use_x3d_subdirs is false
correctionsParameters.x3dModelVersion,
true, // smart,
true); //newAllowed, // save
EyesisCorrections.saveAndShow(
combined_textures[nslice], // imp_texture_cluster,
path,
correctionsParameters.png,
false, // (nslice < 4), // clt_parameters.show_textures,
-1, // jpegQuality){// <0 - keep current, 0 - force Tiff, >0 use for JPEG
1); //
}
}
if (debugLevel > -100) {
return true;
}
......@@ -885,16 +912,21 @@ public class TexturedModel {
final CLTParameters clt_parameters,
ColorProcParameters colorProcParameters,
EyesisCorrectionParameters.RGBParameters rgbParameters,
QuadCLT parameter_scene, // to use for rendering parameters in multi-series sequences
// if null - use reference scene
final QuadCLT [] scenes,
final boolean [] scenes_sel, // null or which scenes to process
final boolean [] selection, // may be null, if not null do not process unselected tiles
final TileCluster [] tileClusters, // disparities, borders, selections for texture passes
// final int margin,
final boolean renormalize, // false - use normalizations from previous scenes to keep consistent colors
final int debug_level)
{
// TODO: ***** scenes with high motion blur also have high ERS to be corrected ! *****
final int ref_index = scenes.length -1;
final QuadCLT ref_scene = scenes[ref_index];
if (parameter_scene == null) {
parameter_scene = ref_scene;
}
final int earliestScene = ref_scene.getEarliestScene(scenes);
final ErsCorrection ers_reference = ref_scene.getErsCorrection();
final int tilesX = ref_scene.getTileProcessor().getTilesX();
......@@ -912,7 +944,23 @@ public class TexturedModel {
final double tex_mb = clt_parameters.tex_mb; // 1.0; // Reduce texture weight if motion blur exceeds this (as square of MB length)
final boolean sharp_alpha = clt_parameters.sharp_alpha;
final boolean is_lwir = ref_scene.isLwir();
final boolean lwir_autorange = is_lwir && colorProcParameters.lwir_autorange;
final boolean tex_um = clt_parameters.tex_um; // imp.um_mono; // TODO: add own parameter
final double tex_um_sigma = clt_parameters.tex_um_sigma; // imp.um_sigma;
final double tex_um_weight = clt_parameters.tex_um_weight; // imp.um_weight;
// TODO: - make texture variants, tex_um_fixed/tex_um_range apply only to unsharp mask, regardless of colors
final boolean lwir_autorange = is_lwir && clt_parameters.tex_lwir_autorange; // colorProcParameters.lwir_autorange;
final boolean tex_um_fixed = clt_parameters.tex_um_fixed; // imp.mono_fixed; // true; // normalize to fixed range when converting to 8 bits
final double tex_um_range = clt_parameters.tex_um_range; // imp.mono_range; // 500.0; // monochrome full-scale range (+/- half)
final boolean tex_hist_norm = clt_parameters.tex_hist_norm; // true;
final double tex_hist_amount = clt_parameters.tex_hist_amount; // clt_parameters. 0.7;
final int tex_hist_bins = clt_parameters.tex_hist_bins; // 1024 ;
final int tex_hist_segments =clt_parameters.tex_hist_segments; // 32 ;
final boolean tex_color = clt_parameters.tex_color; // true;
final int tex_palette = clt_parameters.tex_palette; // 2 ;
ImageDtt image_dtt;
image_dtt = new ImageDtt(
......@@ -932,7 +980,6 @@ public class TexturedModel {
double [][][] inter_weights = new double [num_slices][tilesY][tilesX]; // per-tile texture weights for inter-scene accumulation;
double [][][][][] inter_textures= new double [num_slices][tilesY][tilesX][][]; // [channel][256] - non-overlapping textures
/// double [][] scene_pXpYD;
/// final double disparity_corr = 0.00; // (z_correction == 0) ? 0.0 : geometryCorrection.getDisparityFromZ(1.0/z_correction);
/// TpTask[] tp_tasks_ref = null;
double [][][] ref_pXpYDs = new double [num_slices][][]; // individual for each slice
......@@ -1118,30 +1165,77 @@ public class TexturedModel {
}
}
}
// Optionally apply UM (before auto/manual range)
if (tex_um) {
QuadCLTCPU.umTextures(
faded_textures, // final double [][][] textures, // [nslices][nchn][i]
tilesX * transform_size, // final int width,
tex_um_sigma, // final double um_sigma,
tex_um_weight); // final double um_weight)
}
//renormalize
// normalize all slices together if LWIR
// FIXME: Should it be here? Will setColdHot() change photometric calibration ? Or should it be disabled?
if (lwir_autorange) {
double rel_low = colorProcParameters.lwir_low;
double rel_high = colorProcParameters.lwir_high;
if (!Double.isNaN(ref_scene.getLwirOffset())) {
rel_low -= ref_scene.getLwirOffset();
rel_high -= ref_scene.getLwirOffset();
}
double [] cold_hot = QuadCLTCPU.autorangeTextures(
faded_textures, // double [][][] textures, // [nslices][nchn][i]
rel_low, // double hard_cold,// matches data, DC (this.lwir_offset) subtracted
rel_high, // double hard_hot, // matches data, DC (this.lwir_offset) subtracted
colorProcParameters.lwir_too_cold, // double too_cold, // pixels per image
colorProcParameters.lwir_too_hot, // double too_hot, // pixels per image
1024); // int num_bins)
if (cold_hot != null) {
if (!Double.isNaN(ref_scene.getLwirOffset())) {
cold_hot[0] += ref_scene.getLwirOffset();
cold_hot[1] += ref_scene.getLwirOffset();
double [] norm_table = null; // first try, then make save to properties with cold/hot
if (renormalize) {
if (lwir_autorange) {
double rel_low;
double rel_high;
boolean force_min_max = true;
if (!tex_um && !force_min_max) { // for UM will use min/max
rel_low = colorProcParameters.lwir_low;
rel_high = colorProcParameters.lwir_high;
if (!Double.isNaN(parameter_scene.getLwirOffset())) { // ref_scene or parameter_scene? Or both?
rel_low -= parameter_scene.getLwirOffset();
rel_high -= parameter_scene.getLwirOffset();
}
} else { // for UM need to calculate min and max (probably OK for non-UM too !)
double [] minmax = QuadCLTCPU.getMinMaxTextures(
faded_textures ); //double [][][] textures // [nslices][nchn][i]
rel_low = minmax[0];
rel_high = minmax[1];
}
double [] cold_hot = QuadCLTCPU.autorangeTextures(
faded_textures, // double [][][] textures, // [nslices][nchn][i]
rel_low, // double hard_cold,// matches data, DC (this.lwir_offset) subtracted
rel_high, // double hard_hot, // matches data, DC (this.lwir_offset) subtracted
colorProcParameters.lwir_too_cold, // double too_cold, // pixels per image
colorProcParameters.lwir_too_hot, // double too_hot, // pixels per image
tex_hist_bins); // int num_bins)
if ((cold_hot != null) && !tex_um && !force_min_max) {
if (!Double.isNaN(parameter_scene.getLwirOffset())) {
cold_hot[0] += parameter_scene.getLwirOffset();
cold_hot[1] += parameter_scene.getLwirOffset();
}
}
parameter_scene.setColdHot(cold_hot); // will be used for shifted images and for texture tiles
} else if (tex_um && tex_um_fixed) { // apply fixed range, but for UM only (what about RGB?)
parameter_scene.setColdHot(-0.5*tex_um_range, 0.5*tex_um_range);
}
ref_scene.setColdHot(cold_hot); // will be used for shifted images and for texture tiles
if (tex_hist_norm) { // will normalize (0..1) keeping cold_hot to apply during rendering
// last norm_table element is <=1.0, first >=0;
norm_table = QuadCLTCPU.getHistogramNormalization(
faded_textures, // double [][][] textures, // [nslices][nchn][i]
parameter_scene.getColdHot(), // double [] minmax,
tex_hist_bins, // int num_bins,
tex_hist_segments, //int num_nodes
tex_hist_amount); //double hist_normalize_amount // 1.0 - full
}
}
if (tex_hist_norm && (norm_table != null)) {
// apply histogram normalization
double [] cold_hot = parameter_scene.getColdHot(); // used in linearStackToColor
double [] inverted_table = QuadCLTCPU.invertHistogramNormalization(
norm_table, // double [] direct_table, // last is <1.0, first > 0
tex_hist_bins); // int num_bins)
QuadCLTCPU.applyTexturesNormHist(
faded_textures, // final double [][][] textures, // [nslices][nchn][i]
cold_hot, // final double [] min_max,
inverted_table); // final double [] inv_table)
}
if (debug_level > -1) {
double [][] dbg_textures = new double [faded_textures.length * faded_textures[0].length][faded_textures[0][0].length];
String [] dbg_titles = new String[dbg_textures.length];
......@@ -1181,58 +1275,29 @@ public class TexturedModel {
ref_scene.getImageName()+"-texture_weights");
}
}
for (int nslice = 0; nslice < num_slices; nslice++) {
// See how textures where processed with alpha
/*
// Prepare 4-channel images
ImagePlus [] imps_RGB = new ImagePlus[iclt_fimg.length];
for (int ncam = 0; ncam < iclt_fimg.length; ncam++) if (iclt_fimg[ncam] != null){
String title=String.format("%s%s-%02d",image_name, sAux(), ncam);
imps_RGB[ncam] = linearStackToColor( // probably no need to separate and process the second half with quadCLT_aux (!)
clt_parameters,
colorProcParameters,
rgbParameters,
title, // String name,
"-D"+clt_parameters.disparity, //String suffix, // such as disparity=...
toRGB, // does not work here?
!correctionsParameters.jpeg, // boolean bpp16, // 16-bit per channel color mode for result
false, // true, // boolean saveShowIntermediate, // save/show if set globally
false, // boolean saveShowFinal, // save/show result (color image?)
iclt_fimg[ncam],
out_width,
out_height,
1.0, // scaleExposures[iAux][iSubCam], // double scaleExposure, // is it needed?
-1); // debugLevel );
}
// combine to a sliced color image
int [] slice_seq = {0,1,3,2}; //clockwise
if (imps_RGB.length > 4) {
slice_seq = new int [imps_RGB.length];
for (int i = 0; i < slice_seq.length; i++) {
slice_seq[i] = i;
}
}
int width = imps_RGB[0].getWidth();
int height = imps_RGB[0].getHeight();
ImageStack array_stack=new ImageStack(width,height);
for (int i = 0; i<slice_seq.length; i++) if (imps_RGB[slice_seq[i]] != null){
array_stack.addSlice("port_"+slice_seq[i], imps_RGB[slice_seq[i]].getProcessor().getPixels());
}
ImagePlus imp_stack = new ImagePlus(image_name+sAux()+suffix, array_stack);
imp_stack.getProcessor().resetMinAndMax();
return imp_stack;
*/
// convert to color or apply UM,
double [] minmax = parameter_scene.getColdHot(); // used in linearStackToColor
ImagePlus [] imp_tex = new ImagePlus[num_slices];
EyesisCorrectionParameters.CorrectionParameters correctionsParameters = ref_scene.correctionsParameters;
for (int nslice = 0; nslice < num_slices; nslice++) {
String title=String.format("%s-texture-%02d",ref_scene.getImageName(), nslice);
imp_tex[nslice] = QuadCLTCPU.linearStackToColorLWIR(
clt_parameters, // CLTParameters clt_parameters,
tex_palette, // int lwir_palette, // <0 - do not convert
minmax, // double [] minmax,
title, // String name,
"", // String suffix, // such as disparity=...
tex_color, // boolean toRGB,
faded_textures[nslice], // double [][] texture_data,
tilesX * transform_size, // int width, // int tilesX,
tilesY * transform_size, // int height, // int tilesY,
debug_level); // int debugLevel )
// Add synthetic mesh only with higher resolution? or just any by a specified period?what king of mesh - vertical random, ...
// Split and save as png
}
// Process accumulated textures: average, apply borders, convert to color or apply UM, add synthetic mesh, ...
return null; // ImagePlus[] ? with alpha, to be split into png and saved with alpha.
return imp_tex; // ImagePlus[] ? with alpha, to be split into png and saved with alpha.
}
......
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