Commit 00e707f3 authored by Andrey Filippov's avatar Andrey Filippov

CODEX: Preload scenes for pose RT timing

Co-authored-by: 's avatarCodex <codex@elphel.com>
parent 6d71636c
......@@ -84,10 +84,24 @@ public class CuasRT {
quadCLTs, // QuadCLT [] scenes,
debugLevel); // int debugLevel
final ImagePlus imp_targets = ingest(cuasRanging);
// Exclude storage/TIFF work from the pose RT measurement. This must run BEFORE
// calibratePhotometric(): borrowed Row/Col profiles are expressed under the prior
// corr-xml scales still attached to the scenes here. By Codex on 07/15/2026.
CuasScenePreload scenePreload = null;
if (clt_parameters.curt.pose_test && clt_parameters.curt.pose_raw &&
"none".equals(clt_parameters.curt.kernel_test)) {
scenePreload = CuasScenePreload.load(
quadCLTs, clt_parameters.curt.rowcol_en,
ImageDtt.THREADS_MAX, debugLevel - 2);
if (scenePreload == null) {
System.out.println("CuasRT: ERROR - all-scene preload failed; pose RT measurement NOT RUN (no fallback to per-frame I/O or Row/Col estimation)");
return;
}
}
calibratePhotometric(clt_parameters, master_CLT, quadCLTs, quadCLT_main, ref_index, debugLevel);
appropriateCenter(clt_parameters, master_CLT, debugLevel);
// ---- 2. DIAGNOSTICS (optional, exclusive - replace production and return) ----
if (diagnostics(clt_parameters, master_CLT, quadCLTs, debugLevel)) {
if (diagnostics(clt_parameters, master_CLT, quadCLTs, scenePreload, debugLevel)) {
return;
}
// ---- 3. PRODUCTION: per-scene processing + target detection ----
......@@ -352,6 +366,7 @@ public class CuasRT {
final CLTParameters clt_parameters,
final QuadCLT master_CLT,
final QuadCLT [] quadCLTs,
final CuasScenePreload scenePreload,
final int debugLevel) {
boolean ran = false;
if (!"none".equals(clt_parameters.curt.kernel_test)) {
......@@ -373,6 +388,7 @@ public class CuasRT {
clt_parameters, // CLTParameters clt_parameters,
master_CLT, // QuadCLT center_CLT,
quadCLTs, // QuadCLT [] quadCLTs,
scenePreload, // CuasScenePreload preloaded raw scenes (or null)
debugLevel); // int debugLevel
ran = true;
}
......
/**
** CuasScenePreload.java - all-scene raw LWIR preload for CUAS RT measurements
**
** Copyright (C) 2026 Elphel, Inc.
**
** -----------------------------------------------------------------------------**
**
** CuasScenePreload.java is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** CuasScenePreload.java is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with CuasScenePreload.java. If not, see <http://www.gnu.org/licenses/>.
** -----------------------------------------------------------------------------**
**
*/
package com.elphel.imagej.cuas.rt;
import com.elphel.imagej.cuas.CuasMotion;
import com.elphel.imagej.tileprocessor.QuadCLT;
/**
* Immutable {@code float[scene][sensor][pixel]} raw-scene store. File I/O and,
* for captures made with FLIR Row/Col disabled, application of the already
* calculated borrowed Row/Col profiles happen here before the measured loop.
* This class never estimates Row/Col; future recalculation belongs to the slow
* background calibration process, not to setup or the timed RT path.
*
* The loader must run before CUAS photometric refinement: each saved profile is
* in the calibrated-count domain established by the prior corr-xml scales. The
* exact prior scale is therefore consumed while it is still attached to the
* scene, rather than approximated with the slightly refined current scale.
*
* By Codex on 07/15/2026 from Andrey's RT preload design.
*/
public final class CuasScenePreload {
private final float [][][] scenes;
private final int width;
private final int height;
private final int numSensors;
private final boolean rowColApplied;
private final long bytes;
private CuasScenePreload(
final float [][][] scenes,
final int width,
final int height,
final int numSensors,
final boolean rowColApplied,
final long bytes) {
this.scenes = scenes;
this.width = width;
this.height = height;
this.numSensors = numSensors;
this.rowColApplied = rowColApplied;
this.bytes = bytes;
}
/**
* Read every non-null physical scene. Any incomplete scene or incompatible
* Row/Col provenance aborts the preload as a unit; the RT measurement then does
* not run, rather than silently estimating profiles or timing a partial store.
*/
public static CuasScenePreload load(
final QuadCLT [] quadCLTs,
final boolean rowcol_enable,
final int threadsMax,
final int debugLevel) {
if ((quadCLTs == null) || (quadCLTs.length == 0)) return null;
final long t0 = System.nanoTime();
final float [][][] scenes = new float [quadCLTs.length][][];
int width = 0;
int height = 0;
int numSensors = 0;
int loaded = 0;
long bytes = 0;
System.out.println("CuasScenePreload: reading all raw scenes before the measured loop"+
(rowcol_enable ? " and applying borrowed Row/Col profiles with their prior corr-xml scales" :
" (Row/Col already handled by FLIR or explicitly disabled)"));
for (int nscene = 0; nscene < quadCLTs.length; nscene++) {
final QuadCLT scene = quadCLTs[nscene];
if (scene == null) continue;
final double [][][] raw = CuasMotion.readRawImageData(scene, threadsMax, debugLevel);
if ((raw == null) || (raw.length == 0) || (raw[0] == null) || (raw[0][0] == null)) {
System.out.println("CuasScenePreload: FAILED at scene "+nscene+" ("+
scene.getImageName()+") - preload discarded");
return null;
}
final int sceneWidth = scene.getGeometryCorrection().getSensorWH()[0];
if ((sceneWidth <= 0) || ((raw[0][0].length % sceneWidth) != 0)) {
System.out.println("CuasScenePreload: invalid geometry at scene "+nscene+
" - preload discarded");
return null;
}
final int sceneHeight = raw[0][0].length / sceneWidth;
if (loaded == 0) {
width = sceneWidth;
height = sceneHeight;
numSensors = raw.length;
} else if ((sceneWidth != width) || (sceneHeight != height) || (raw.length != numSensors)) {
System.out.println("CuasScenePreload: incompatible dimensions at scene "+nscene+
" - preload discarded");
return null;
}
// Validate the complete mono scene before changing any pixel. This keeps a
// failed preload atomic and prevents silently ignoring unexpected channels.
for (int s = 0; s < numSensors; s++) {
if ((raw[s] == null) || (raw[s].length != 1) || (raw[s][0] == null) ||
(raw[s][0].length != width*height)) {
System.out.println("CuasScenePreload: incomplete or non-mono sensor "+s+
" at scene "+nscene+" - preload discarded");
return null;
}
}
if (rowcol_enable) {
final double [] scales2 = scene.getLwirScales2();
if (scales2 != null) for (int s = 0; s < scales2.length; s++) {
if (scales2[s] != 0.0) {
System.out.println("CuasScenePreload: nonzero prior scales2["+s+"]="+scales2[s]+
" cannot map calibrated Row/Col profiles back to raw - preload discarded");
return null;
}
}
try {
CuasConditioning.applyCalibratedRowColToRaw(
raw, sceneWidth, scene.getRowCorr(), scene.getColCorr(),
scene.getLwirScales().clone());
} catch (IllegalArgumentException e) {
System.out.println("CuasScenePreload: "+e.getMessage()+" at scene "+nscene+" ("+
scene.getImageName()+") - preload discarded");
return null;
}
}
final float [][] fscene = new float [numSensors][];
for (int s = 0; s < numSensors; s++) {
fscene[s] = new float [raw[s][0].length];
for (int i = 0; i < fscene[s].length; i++) fscene[s][i] = (float) raw[s][0][i];
bytes += (long) Float.BYTES * fscene[s].length;
}
scenes[nscene] = fscene;
loaded++;
if (((loaded % 25) == 0) || (nscene == quadCLTs.length - 1)) {
System.out.println(String.format(
"CuasScenePreload: %d scenes loaded, %.3f GiB resident",
loaded, bytes/(1024.0*1024.0*1024.0)));
}
}
if (loaded == 0) return null;
final double seconds = (System.nanoTime() - t0)*1.0e-9;
System.out.println(String.format(
"CuasScenePreload: READY %d scenes, %d sensors, %dx%d, %.3f GiB in %.3f s; file I/O is outside the measured loop",
loaded, numSensors, width, height, bytes/(1024.0*1024.0*1024.0), seconds));
return new CuasScenePreload(scenes, width, height, numSensors, rowcol_enable, bytes);
}
public float [][] getScene(final int index) {
return ((index >= 0) && (index < scenes.length)) ? scenes[index] : null;
}
public float [][] createScratch() {
final float [][] scratch = new float [numSensors][];
for (int s = 0; s < numSensors; s++) scratch[s] = new float [width*height];
return scratch;
}
public boolean isRowColApplied() { return rowColApplied; }
public int getWidth() { return width; }
public int getHeight() { return height; }
public int getNumSensors() { return numSensors; }
public long getBytes() { return bytes; }
}
......@@ -1573,6 +1573,26 @@ public class GpuQuad{ // quad camera description
}
}
/**
* Copy pre-combined single-channel float images without rebuilding them from
* double Java arrays. This is the host contract used by the preloaded LWIR RT
* harness. By Codex on 07/15/2026.
*/
public void setBayerImages(
float [] [] bayer_data,
boolean force) {
if (this.gpuTileProcessor.bayer_set && !force) return;
for (int ncam = 0; ncam < bayer_data.length; ncam++) {
if (bayer_data[ncam] == null) {
System.out.println("BUG!! bayer_data["+ncam+"] == null");
continue;
}
setBayerImage(bayer_data[ncam], ncam);
}
this.gpuTileProcessor.bayer_set = true;
if (getGpu_debug_level() > -1) System.out.println("======setBayerImages(float)");
}
// Set same image to all sensors (for reverse conversion)
public void setBayerImage(
double [][] bayer_data,
......
......@@ -30,7 +30,7 @@ import com.sun.jna.Pointer;
/**
* Full Java -> JNA -> NVRTC gate for the LWIR conditioning component. It runs
* the production dimensions (16 x 640 x 512), verifies the double kernel
* bit-for-bit against the current {@code CuasConditioning.condition()} linear
* bit-for-bit against the current {@code CuasConditioning.conditionLinear()} linear
* expression, verifies the float kernel bit-for-bit against {@link Math#fma},
* characterizes float-map error, and measures both synchronized kernel calls
* and the current 16-array JNA upload plus kernel path.
......
......@@ -303,6 +303,13 @@ public class GpuQuadJna extends GpuQuad {
}
jna_bayer_set = true; // By Claude on 07/02/2026: match base (gpuTileProcessor.bayer_set = true)
}
@Override public void setBayerImages(float[][] bayer_data, boolean force) {
if (jna_bayer_set && !force) return;
for (int ncam = 0; ncam < bayer_data.length; ncam++) {
if (bayer_data[ncam] != null) lib.tp_proc_set_image(proc, ncam, bayer_data[ncam]);
}
jna_bayer_set = true;
}
// sum the (1 or 3) split-color channels into one image, as GpuQuad.setBayerImages does
private static float[] combineChannels(double[][] chans) {
if (chans == null || chans[0] == null) return null;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment