Commit 54d842f0 authored by Andrey Filippov's avatar Andrey Filippov

CLAUDE: in-process native DNN backend (CuasDnnLocal) via JNA - no server

Piece 3 of the native-JNA DNN path. Adds a local backend that runs the SAME
L1+L2 inference as CuasDnnRemote but in-process via LibTorch (libtpdnn.so / JNA),
so the CUAS pipeline runs without the DGX or any Python server:
  - CuasDnnBackend  : shared interface (upload/getNFrames/inferBatch->BatchResult/close)
  - TpDnnJna        : JNA Library binding libtpdnn.so's C-ABI
  - CuasDnnLocal    : wraps it; reads N/P/vr/l2_ch from each model's bundled .meta.json
                      (single source of truth), float[][]<->float[], builds BatchResult
  - CuasDnnRemote   : now implements CuasDnnBackend (signatures unchanged)
  - CuasDetectRT    : DNN path gate now fires on (curt_dnn_remote || curt_dnn_local);
                      backend = local? CuasDnnLocal : CuasDnnRemote; ensureServer skipped
                      when local; local-CPU-ORT gate also excludes curt_dnn_local (no
                      double-run). runDnnRemote loop unchanged.
  - IntersceneMatchParameters: curt_dnn_local (flag) + curt_dnn_local_dir (model dir
                      override; empty = bundled /cuas_dnn resource) + GUI labels/persist.

Validated: full Java->JNA->libtpdnn vs the Python-server oracle = EXACT
(offset5=0.0, roi=0.0, nch=6). mvn -DskipTests package OK.

Runtime: -Djna.library.path=<dir with libtpdnn.so>; libtpdnn.so finds libtorch via
its rpath. Model resolution mirrors CuasDnnRemote's bundled-vs-override scheme.
Co-Authored-By: 's avatarClaude Opus 4.8 (1M context) <noreply@anthropic.com>
parent 9258b5d9
...@@ -748,9 +748,12 @@ public class CuasDetectRT { ...@@ -748,9 +748,12 @@ public class CuasDetectRT {
final boolean synth_bg = clt_parameters.imp.curt_synth_bg; final boolean synth_bg = clt_parameters.imp.curt_synth_bg;
final int n_synth = synth ? synth_pixels.length : 0; final int n_synth = synth ? synth_pixels.length : 0;
try { // auto-launch the DGX server if not already running (deploy bundled/override scripts, ssh-start, poll) // By Claude on 06/20/2026 try { // auto-launch the DGX server if not already running (deploy bundled/override scripts, ssh-start, poll) // By Claude on 06/20/2026
if (!clt_parameters.imp.curt_dnn_local) // local native backend has no server to launch. By Claude on 06/27/2026
CuasDnnRemote.ensureServer(clt_parameters.imp.curt_dnn_remote_host, clt_parameters.imp.curt_dnn_remote_model, l2model, clt_parameters.imp.curt_dnn_remote_srcdir); CuasDnnRemote.ensureServer(clt_parameters.imp.curt_dnn_remote_host, clt_parameters.imp.curt_dnn_remote_model, l2model, clt_parameters.imp.curt_dnn_remote_srcdir);
} catch (Exception e) { System.out.println("runDnnRemote(): server auto-launch failed: "+e); } } catch (Exception e) { System.out.println("runDnnRemote(): server auto-launch failed: "+e); }
try (CuasDnnRemote remote = new CuasDnnRemote(clt_parameters.imp.curt_dnn_remote_host)) { try (CuasDnnBackend remote = clt_parameters.imp.curt_dnn_local
? new CuasDnnLocal(clt_parameters.imp.curt_dnn_local_dir, clt_parameters.imp.curt_dnn_remote_model, l2model)
: new CuasDnnRemote(clt_parameters.imp.curt_dnn_remote_host)) {
// Build the upload array: LoG-conditioned real (optionally synth_bg_avg-decimated upstream), optionally // Build the upload array: LoG-conditioned real (optionally synth_bg_avg-decimated upstream), optionally
// with synthetic targets mixed in (tiled synth[t % n_synth]; clean = zero bg first) - matching the local // with synthetic targets mixed in (tiled synth[t % n_synth]; clean = zero bg first) - matching the local
// per-level injection (CuasDetectRT.java synth block) but PRE-pyramid; the DGX then averages it into levels. // per-level injection (CuasDetectRT.java synth block) but PRE-pyramid; the DGX then averages it into levels.
...@@ -1150,7 +1153,7 @@ public class CuasDetectRT { ...@@ -1150,7 +1153,7 @@ public class CuasDetectRT {
// same shape as the C5P output, saved as -DNN-RECT / -DNN-HYPER-RECT. N (temporal // same shape as the C5P output, saved as -DNN-RECT / -DNN-HYPER-RECT. N (temporal
// depth, 8 or 9) is read from the loaded ONNX. Reuses curt_c5_levels for level gating. Recurrent feed is the next step // depth, 8 or 9) is read from the loaded ONNX. Reuses curt_c5_levels for level gating. Recurrent feed is the next step
// (the field is [0,1]-scaled, unlike the C5P matched-filter response - needs rescale). // (the field is [0,1]-scaled, unlike the C5P matched-filter response - needs rescale).
if (!clt_parameters.imp.curt_dnn_model.isEmpty() && (curt_save_select != null) && !clt_parameters.imp.curt_dnn_remote) { // local CPU path (remote = DGX block below) // By Claude on 06/13/2026, remote guard 06/20/2026 if (!clt_parameters.imp.curt_dnn_model.isEmpty() && (curt_save_select != null) && !clt_parameters.imp.curt_dnn_remote && !clt_parameters.imp.curt_dnn_local) { // local CPU path (remote=DGX / curt_dnn_local=native LibTorch blocks below) // By Claude on 06/13/2026, remote guard 06/20/2026, local guard 06/27/2026
final int vr_dnn = clt_parameters.imp.curt_vel_radius; // By Claude on 06/13/2026 final int vr_dnn = clt_parameters.imp.curt_vel_radius; // By Claude on 06/13/2026
final int dnn_stride = Math.max(1, clt_parameters.imp.curt_dnn_stride); // 1 = every slice (testing), 4 = production 50% overlap // By Claude on 06/14/2026 final int dnn_stride = Math.max(1, clt_parameters.imp.curt_dnn_stride); // 1 = every slice (testing), 4 = production 50% overlap // By Claude on 06/14/2026
try { // By Claude on 06/13/2026 try { // By Claude on 06/13/2026
...@@ -1317,7 +1320,7 @@ public class CuasDetectRT { ...@@ -1317,7 +1320,7 @@ public class CuasDetectRT {
} // By Claude on 06/13/2026 } // By Claude on 06/13/2026
} // By Claude on 06/13/2026 } // By Claude on 06/13/2026
// Remote DGX inference path (curt_dnn_remote): upload conditioned stack, DGX builds pyramid + infers. // By Claude on 06/20/2026 // Remote DGX inference path (curt_dnn_remote): upload conditioned stack, DGX builds pyramid + infers. // By Claude on 06/20/2026
if ((curt_save_select != null) && clt_parameters.imp.curt_dnn_remote) { // model lives on the DGX (run dir) - no local curt_dnn_model needed // By Claude on 06/20/2026 if ((curt_save_select != null) && (clt_parameters.imp.curt_dnn_remote || clt_parameters.imp.curt_dnn_local)) { // DGX server OR in-process native LibTorch (curt_dnn_local). By Claude on 06/27/2026
runDnnRemote(clt_parameters, cuasRTUtils, dpixels_log, ts_pyramid, pyramid_levels, c5_levels, curt_save_select, title_conv5d); // By Claude on 06/20/2026 runDnnRemote(clt_parameters, cuasRTUtils, dpixels_log, ts_pyramid, pyramid_levels, c5_levels, curt_save_select, title_conv5d); // By Claude on 06/20/2026
} }
System.out.println(now()+" detectTargets(): done"); // By Claude on 06/11/2026 System.out.println(now()+" detectTargets(): done"); // By Claude on 06/11/2026
......
package com.elphel.imagej.cuas.rt;
import java.awt.Rectangle;
/**
* DNN inference backend for the CUAS L1+L2 pipeline. Implemented by {@link CuasDnnRemote} (TCP to the
* Python infer_server, e.g. on the DGX) and {@link CuasDnnLocal} (in-process native LibTorch via JNA /
* libtpdnn.so). CuasDetectRT.runDnnRemote() talks to this interface so the two are drop-in swappable;
* the result type {@link CuasDnnRemote.BatchResult} is shared. By Claude on 06/27/2026.
*/
public interface CuasDnnBackend extends AutoCloseable {
/** Upload the conditioned stack frames[T][H*W] (row-major) once; the backend builds the temporal
* pyramid. Returns the per-level frame counts (and sets {@link #getNFrames()}). */
int [] upload(float [][] frames, int H, int W) throws Exception;
/** Temporal depth N of the loaded model (window length), known after {@link #upload}. */
int getNFrames();
/** Infer `count` scenes of a level in one call (newest_s = start + s*stride). rmaxCells>0 enables the
* on-GPU ghostbuster. l2Enable/l2Reset drive Layer-2; noiseScale is the per-level L1-input scale. */
CuasDnnRemote.BatchResult inferBatch(int level, int start, int count, int stride, Rectangle roi,
double rmaxCells, boolean l2Enable, boolean l2Reset, double noiseScale) throws Exception;
@Override
void close();
}
package com.elphel.imagej.cuas.rt;
import java.awt.Rectangle;
import java.io.File;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
/**
* In-process native DNN backend: runs the SAME L1+L2 inference as {@link CuasDnnRemote}, but via
* LibTorch through JNA (libtpdnn.so, tile_processor_gpu/jna/tp_dnn.cpp) instead of TCP to a Python
* server. No DGX / no server process. Each model's params (N/P/vr/out_ch, L2 ch_hidden) come from its
* bundled {@code <model>.ts.pt.meta.json} sidecar (single source of truth). By Claude on 06/27/2026.
*
* Model resolution mirrors CuasDnnRemote's bundled-vs-override convention:
* - localDir set -> &lt;localDir&gt;/&lt;model&gt;/model.ts.pt (+ .meta.json) [dev/override]
* - localDir empty -> bundled jar resource /cuas_dnn/&lt;basename&gt;/model.ts.pt extracted to temp
*
* Requires {@code -Djna.library.path=<dir with libtpdnn.so>}; libtpdnn.so finds libtorch via its rpath
* (set by build_dnn.sh) or LD_LIBRARY_PATH.
*/
public class CuasDnnLocal implements CuasDnnBackend {
private static TpDnnJna LIB = null;
private static synchronized TpDnnJna lib() {
if (LIB == null) LIB = Native.load("tpdnn", TpDnnJna.class);
return LIB;
}
private final TpDnnJna lib;
private final Pointer ctx;
private final int N, P, vr, nvel;
private int nFrames = 0;
private int H = 0, W = 0;
private File tmpDir = null; // holds extracted bundled models (deleted on close)
/** @param localDir override dir holding &lt;model&gt;/model.ts.pt, or empty/null to use bundled resources.
* @param l1Model L1 run name (e.g. "runs/weighted9_pm_s").
* @param l2Model L2 run name (e.g. "runs/mexhat_gaps_boost40"), or empty/null for L1-only. */
public CuasDnnLocal(String localDir, String l1Model, String l2Model) throws Exception {
this.lib = lib();
String [] l1 = resolveModel(localDir, l1Model, ".ts.pt"); // {tsPath, metaJson}
this.N = metaInt(l1[1], "N", 9);
this.P = metaInt(l1[1], "P", 24);
this.vr = metaInt(l1[1], "vr", 5);
this.nvel = (2 * vr + 1) * (2 * vr + 1);
String l2ts = "";
int l2ch = 24;
if ((l2Model != null) && !l2Model.isEmpty()) {
String [] l2 = resolveModel(localDir, l2Model, ".l2.ts.pt");
l2ts = l2[0];
l2ch = metaInt(l2[1], "ch_hidden", 24);
}
this.ctx = lib.tpdnn_init(l1[0], l2ts, N, P, vr, l2ch);
if (ctx == null) throw new Exception("tpdnn_init failed (l1=" + l1[0] + ", l2=" + l2ts + ")");
System.out.println("CuasDnnLocal: native LibTorch backend up (L1=" + l1Model
+ ", L2=" + ((l2Model == null || l2Model.isEmpty()) ? "off" : l2Model)
+ "; N=" + N + " P=" + P + " vr=" + vr + " l2_ch=" + l2ch + ")");
}
@Override
public int [] upload(float [][] frames, int H, int W) throws Exception {
this.H = H; this.W = W;
int T = frames.length, hw = H * W;
float [] flat = new float [T * hw]; // [T,H,W] row-major
for (int t = 0; t < T; t++) System.arraycopy(frames[t], 0, flat, t * hw, hw);
lib.tpdnn_upload(ctx, flat, T, H, W);
this.nFrames = N;
int nl = lib.tpdnn_num_levels(ctx);
int [] counts = new int [nl];
for (int i = 0; i < nl; i++) counts[i] = lib.tpdnn_level_frames(ctx, i);
return counts;
}
@Override
public int getNFrames() { return nFrames; }
@Override
public CuasDnnRemote.BatchResult inferBatch(int level, int start, int count, int stride, Rectangle roi,
double rmaxCells, boolean l2Enable, boolean l2Reset, double noiseScale) throws Exception {
int hw = H * W, rn = roi.width * roi.height;
int nchAlloc = l2Enable ? 6 : 5; // native returns 6 with L2 (+age), else 5
float [] o5 = new float [count * nchAlloc * hw];
float [] rfb = new float [count * rn * nvel];
long t0 = System.nanoTime();
int nch = lib.tpdnn_infer(ctx, level, start, count, stride,
roi.x, roi.y, roi.width, roi.height, rmaxCells,
l2Enable ? 1 : 0, l2Reset ? 1 : 0, noiseScale, o5, rfb);
double ms = (System.nanoTime() - t0) / 1.0e6;
CuasDnnRemote.BatchResult r = new CuasDnnRemote.BatchResult();
r.gpuMs = ms; r.H = H; r.W = W; r.count = count; r.nch = nch; r.nvel = nvel;
r.rh = roi.height; r.rw = roi.width;
r.offset5 = new float [count][nch][hw]; // o5 layout [count][nch][hw]
for (int s = 0; s < count; s++)
for (int c = 0; c < nch; c++)
System.arraycopy(o5, (s * nch + c) * hw, r.offset5[s][c], 0, hw);
r.roiField = new float [count][rn][nvel]; // rfb layout [count][rn][nvel]
for (int s = 0; s < count; s++)
for (int p = 0; p < rn; p++)
System.arraycopy(rfb, (s * rn + p) * nvel, r.roiField[s][p], 0, nvel);
return r;
}
@Override
public void close() {
try { if (ctx != null) lib.tpdnn_free(ctx); } catch (Exception e) { /* ignore */ }
if (tmpDir != null) { try { deleteRec(tmpDir); } catch (Exception e) { /* ignore */ } }
}
// ---- model resolution + tiny meta parser ----
/** Returns {tsPath, metaJsonContent} for the model. localDir override, else bundled resource->temp. */
private String [] resolveModel(String localDir, String model, String suffix) throws Exception {
String fileName = "model" + suffix; // model.ts.pt / model.l2.ts.pt
if ((localDir != null) && !localDir.isEmpty()) {
File ts = new File(new File(localDir, model), fileName);
File meta = new File(ts.getPath() + ".meta.json");
if (!ts.exists()) throw new Exception("local model not found: " + ts);
String mj = meta.exists() ? new String(Files.readAllBytes(meta.toPath())) : "";
return new String [] { ts.getPath(), mj };
}
// bundled: /cuas_dnn/<basename(model)>/model.ts.pt (+ .meta.json) -> extract to a temp dir
String base = model.substring(model.lastIndexOf('/') + 1);
if (tmpDir == null) tmpDir = Files.createTempDirectory("cuas_dnn_").toFile();
String res = "/cuas_dnn/" + base + "/" + fileName;
Path tsTmp = extractResource(res, base + suffix);
String mj = "";
try (InputStream is = CuasDnnLocal.class.getResourceAsStream(res + ".meta.json")) {
if (is != null) mj = new String(is.readAllBytes());
}
return new String [] { tsTmp.toString(), mj };
}
private Path extractResource(String res, String tmpName) throws Exception {
try (InputStream is = CuasDnnLocal.class.getResourceAsStream(res)) {
if (is == null) throw new Exception("bundled resource not found: " + res);
Path out = new File(tmpDir, tmpName).toPath();
Files.copy(is, out, StandardCopyOption.REPLACE_EXISTING);
return out;
}
}
private static int metaInt(String json, String key, int dflt) {
if (json == null) return dflt;
Matcher m = Pattern.compile("\"" + key + "\"\\s*:\\s*(-?\\d+)").matcher(json);
return m.find() ? Integer.parseInt(m.group(1)) : dflt;
}
private static void deleteRec(File f) {
File [] kids = f.listFiles();
if (kids != null) for (File k : kids) deleteRec(k);
f.delete();
}
}
...@@ -28,7 +28,7 @@ import java.nio.file.Files; ...@@ -28,7 +28,7 @@ import java.nio.file.Files;
* READBACK: cmd=3, int level, frame -> reply: int H,W, H*W float32 (one conditioned pyramid frame) * READBACK: cmd=3, int level, frame -> reply: int H,W, H*W float32 (one conditioned pyramid frame)
* BYE : cmd=0 * BYE : cmd=0
*/ */
public class CuasDnnRemote implements AutoCloseable { public class CuasDnnRemote implements CuasDnnBackend {
private static final int CMD_BYE = 0, CMD_UPLOAD = 1, CMD_INFER = 2, CMD_READBACK = 3, CMD_STATUS = 4; private static final int CMD_BYE = 0, CMD_UPLOAD = 1, CMD_INFER = 2, CMD_READBACK = 3, CMD_STATUS = 4;
private final Socket sock; private final Socket sock;
private final DataInputStream in; private final DataInputStream in;
......
package com.elphel.imagej.cuas.rt;
import com.sun.jna.Library;
import com.sun.jna.Pointer;
/**
* JNA boundary to the native LibTorch DNN inference library (libtpdnn.so, tile_processor_gpu/jna/tp_dnn.cpp).
* Mirrors the C-ABI: load TorchScript L1(+L2), build the temporal pyramid from an uploaded stack, and run
* shift-and-stitch + decode (+ L2 recurrence/age) per level. Loaded by base name "tpdnn" via
* {@code Native.load} with {@code -Djna.library.path=<dir with libtpdnn.so>}. By Claude on 06/27/2026.
*/
public interface TpDnnJna extends Library {
/** Load TorchScript L1 (+optional L2) onto CUDA. l2_path empty/NULL -> L1-only. Returns an opaque ctx. */
Pointer tpdnn_init(String l1_path, String l2_path, int N, int P, int vr, int l2_ch);
/** Build the temporal pyramid from host log [T,H,W] (row-major float32). */
void tpdnn_upload(Pointer ctx, float[] log, int T, int H, int W);
int tpdnn_num_levels(Pointer ctx);
int tpdnn_level_frames(Pointer ctx, int level);
/** One INFER over `count` scenes of `level`. Writes offset5 into out_o5 [count*nch*H*W] and roi into
* out_roi [count*rh*rw*nvel] (native float order). Returns nch (5 L1-only, 6 with L2:+age). */
int tpdnn_infer(Pointer ctx, int level, int start, int count, int stride,
int rx, int ry, int rw, int rh, double rmax,
int l2_enable, int l2_reset, double noise_scale,
float[] out_o5, float[] out_roi);
void tpdnn_free(Pointer ctx);
}
...@@ -1168,6 +1168,8 @@ min_str_neib_fpn 0.35 ...@@ -1168,6 +1168,8 @@ min_str_neib_fpn 0.35
public String curt_dnn_remote_host = "192.168.0.62:5577"; // DGX inference server host:port for curt_dnn_remote (see attic/imagej-elphel-internal/c5p_dnn/infer_server.py) // By Claude on 06/20/2026 public String curt_dnn_remote_host = "192.168.0.62:5577"; // DGX inference server host:port for curt_dnn_remote (see attic/imagej-elphel-internal/c5p_dnn/infer_server.py) // By Claude on 06/20/2026
public String curt_dnn_remote_model = "runs/weighted9_pm_s"; // DGX-side run dir (with model.pt) the auto-launched server loads (passed as RUN=) // By Claude on 06/20/2026 public String curt_dnn_remote_model = "runs/weighted9_pm_s"; // DGX-side run dir (with model.pt) the auto-launched server loads (passed as RUN=) // By Claude on 06/20/2026
public String curt_dnn_remote_srcdir = ""; // server-scripts override dir: empty = bundled jar resource (cuas_dnn/), set = local dir - same default-vs-override scheme as the GPU kernels (cuda_project_directory) // By Claude on 06/20/2026 public String curt_dnn_remote_srcdir = ""; // server-scripts override dir: empty = bundled jar resource (cuas_dnn/), set = local dir - same default-vs-override scheme as the GPU kernels (cuda_project_directory) // By Claude on 06/20/2026
public boolean curt_dnn_local = false; // run the DNN front-end in-process via native LibTorch (CuasDnnLocal/libtpdnn.so) instead of the remote Python server - identical L1+L2 result, no DGX/server. Overrides curt_dnn_remote when set. // By Claude on 06/27/2026
public String curt_dnn_local_dir = ""; // override dir holding <model>/model.ts.pt (+.meta.json) for curt_dnn_local: empty = bundled jar resource (cuas_dnn/) - same default-vs-override scheme as the kernels. // By Claude on 06/27/2026
public boolean curt_dnn_l2 = false; // run the trained Layer-2 (track-before-detect ConvGRU) on the DGX after L1: -OFFSET then carries L2 {det,Vx,Vy} (L1's full-res, NON-ghostbusted field fed in, recurrence over the scene/time axis). Off = L1 offset5 as before (re-run with this off to inspect L1). Requires curt_dnn_remote. // By Claude on 06/22/2026 public boolean curt_dnn_l2 = false; // run the trained Layer-2 (track-before-detect ConvGRU) on the DGX after L1: -OFFSET then carries L2 {det,Vx,Vy} (L1's full-res, NON-ghostbusted field fed in, recurrence over the scene/time axis). Off = L1 offset5 as before (re-run with this off to inspect L1). Requires curt_dnn_remote. // By Claude on 06/22/2026
public String curt_dnn_l2_model = "runs/l2_v1"; // DGX-side Layer-2 run dir (with model.pt) the auto-launched server loads (passed as RUN2=) // By Claude on 06/22/2026 public String curt_dnn_l2_model = "runs/l2_v1"; // DGX-side Layer-2 run dir (with model.pt) the auto-launched server loads (passed as RUN2=) // By Claude on 06/22/2026
public boolean curt_dnn_recur_splat = false; // when feeding the DNN field to the recurrent layer: false = feed per-pixel field as-is; true = splat each pixel's velocity vector to its fractional offset (px+dx,py+dy) so neighbours reinforce in one sub-pixel bin // By Claude on 06/14/2026 public boolean curt_dnn_recur_splat = false; // when feeding the DNN field to the recurrent layer: false = feed per-pixel field as-is; true = splat each pixel's velocity vector to its fractional offset (px+dx,py+dy) so neighbours reinforce in one sub-pixel bin // By Claude on 06/14/2026
...@@ -3480,6 +3482,10 @@ min_str_neib_fpn 0.35 ...@@ -3480,6 +3482,10 @@ min_str_neib_fpn 0.35
"DGX-side run directory (containing model.pt) the auto-launched server loads, e.g. runs/weighted9_pm_s."); "DGX-side run directory (containing model.pt) the auto-launched server loads, e.g. runs/weighted9_pm_s.");
gd.addStringField ("DNN remote server src (empty=bundled)", this.curt_dnn_remote_srcdir, 40, // By Claude on 06/20/2026 gd.addStringField ("DNN remote server src (empty=bundled)", this.curt_dnn_remote_srcdir, 40, // By Claude on 06/20/2026
"Override dir for the DGX server scripts (infer_server.py / run_infer_server.sh): empty = bundled jar resource (cuas_dnn/); set = local dir. Same default-vs-override (bundled resource vs local repo) scheme as the GPU kernels - bundled is the working version, refresh it after server-script dev."); "Override dir for the DGX server scripts (infer_server.py / run_infer_server.sh): empty = bundled jar resource (cuas_dnn/); set = local dir. Same default-vs-override (bundled resource vs local repo) scheme as the GPU kernels - bundled is the working version, refresh it after server-script dev.");
gd.addCheckbox ("DNN local (in-process LibTorch)", this.curt_dnn_local, // By Claude on 06/27/2026
"Run the DNN front-end in-process via native LibTorch (libtpdnn.so / JNA) instead of the remote Python server - identical L1+L2 result, no DGX/server. When set, overrides 'DNN remote'. Needs -Djna.library.path pointing at libtpdnn.so.");
gd.addStringField ("DNN local model dir (empty=bundled)", this.curt_dnn_local_dir, 40, // By Claude on 06/27/2026
"Override dir holding <model>/model.ts.pt (+ .meta.json) for 'DNN local': empty = bundled jar resource (cuas_dnn/). Same default-vs-override scheme as the kernels. Uses the same model names as the remote fields.");
gd.addCheckbox ("DNN Layer-2 (run on DGX)", this.curt_dnn_l2, // By Claude on 06/22/2026 gd.addCheckbox ("DNN Layer-2 (run on DGX)", this.curt_dnn_l2, // By Claude on 06/22/2026
"Run the trained Layer-2 track-before-detect ConvGRU on the DGX after L1 (requires 'DNN remote'). -OFFSET then shows L2 {det,Vx,Vy} (L1's full-res non-ghostbusted field fed in, recurrence over time), titled -DNN-L2-. Uncheck to re-run the old L1 way."); // By Claude on 06/22/2026 "Run the trained Layer-2 track-before-detect ConvGRU on the DGX after L1 (requires 'DNN remote'). -OFFSET then shows L2 {det,Vx,Vy} (L1's full-res non-ghostbusted field fed in, recurrence over time), titled -DNN-L2-. Uncheck to re-run the old L1 way."); // By Claude on 06/22/2026
gd.addStringField ("DNN Layer-2 model (DGX run dir)", this.curt_dnn_l2_model, 24, // By Claude on 06/22/2026 gd.addStringField ("DNN Layer-2 model (DGX run dir)", this.curt_dnn_l2_model, 24, // By Claude on 06/22/2026
...@@ -5032,6 +5038,8 @@ min_str_neib_fpn 0.35 ...@@ -5032,6 +5038,8 @@ min_str_neib_fpn 0.35
this.curt_dnn_remote_host = gd.getNextString().trim(); // By Claude on 06/20/2026 this.curt_dnn_remote_host = gd.getNextString().trim(); // By Claude on 06/20/2026
this.curt_dnn_remote_model = gd.getNextString().trim(); // By Claude on 06/20/2026 this.curt_dnn_remote_model = gd.getNextString().trim(); // By Claude on 06/20/2026
this.curt_dnn_remote_srcdir = gd.getNextString().trim(); // By Claude on 06/20/2026 this.curt_dnn_remote_srcdir = gd.getNextString().trim(); // By Claude on 06/20/2026
this.curt_dnn_local = gd.getNextBoolean(); // By Claude on 06/27/2026
this.curt_dnn_local_dir = gd.getNextString().trim(); // By Claude on 06/27/2026
this.curt_dnn_l2 = gd.getNextBoolean(); // By Claude on 06/22/2026 this.curt_dnn_l2 = gd.getNextBoolean(); // By Claude on 06/22/2026
this.curt_dnn_l2_model = gd.getNextString().trim(); // By Claude on 06/22/2026 this.curt_dnn_l2_model = gd.getNextString().trim(); // By Claude on 06/22/2026
this.curt_dnn_thresh = gd.getNextNumber(); // By Claude on 06/13/2026 this.curt_dnn_thresh = gd.getNextNumber(); // By Claude on 06/13/2026
...@@ -6401,6 +6409,8 @@ min_str_neib_fpn 0.35 ...@@ -6401,6 +6409,8 @@ min_str_neib_fpn 0.35
properties.setProperty(prefix+"curt_dnn_remote_host", this.curt_dnn_remote_host); // String // By Claude on 06/20/2026 properties.setProperty(prefix+"curt_dnn_remote_host", this.curt_dnn_remote_host); // String // By Claude on 06/20/2026
properties.setProperty(prefix+"curt_dnn_remote_model", this.curt_dnn_remote_model); // String // By Claude on 06/20/2026 properties.setProperty(prefix+"curt_dnn_remote_model", this.curt_dnn_remote_model); // String // By Claude on 06/20/2026
properties.setProperty(prefix+"curt_dnn_remote_srcdir", this.curt_dnn_remote_srcdir); // String // By Claude on 06/20/2026 properties.setProperty(prefix+"curt_dnn_remote_srcdir", this.curt_dnn_remote_srcdir); // String // By Claude on 06/20/2026
properties.setProperty(prefix+"curt_dnn_local", this.curt_dnn_local+""); // boolean // By Claude on 06/27/2026
properties.setProperty(prefix+"curt_dnn_local_dir", this.curt_dnn_local_dir); // String // By Claude on 06/27/2026
properties.setProperty(prefix+"curt_dnn_l2", this.curt_dnn_l2+""); // boolean // By Claude on 06/22/2026 properties.setProperty(prefix+"curt_dnn_l2", this.curt_dnn_l2+""); // boolean // By Claude on 06/22/2026
properties.setProperty(prefix+"curt_dnn_l2_model", this.curt_dnn_l2_model); // String // By Claude on 06/22/2026 properties.setProperty(prefix+"curt_dnn_l2_model", this.curt_dnn_l2_model); // String // By Claude on 06/22/2026
properties.setProperty(prefix+"curt_synth_src", this.curt_synth_src+""); // boolean // By Claude on 06/11/2026 properties.setProperty(prefix+"curt_synth_src", this.curt_synth_src+""); // boolean // By Claude on 06/11/2026
...@@ -6798,6 +6808,8 @@ min_str_neib_fpn 0.35 ...@@ -6798,6 +6808,8 @@ min_str_neib_fpn 0.35
if (properties.getProperty(prefix+"curt_dnn_remote_host")!=null) this.curt_dnn_remote_host=(String) properties.getProperty(prefix+"curt_dnn_remote_host"); // By Claude on 06/20/2026 if (properties.getProperty(prefix+"curt_dnn_remote_host")!=null) this.curt_dnn_remote_host=(String) properties.getProperty(prefix+"curt_dnn_remote_host"); // By Claude on 06/20/2026
if (properties.getProperty(prefix+"curt_dnn_remote_model")!=null) this.curt_dnn_remote_model=(String) properties.getProperty(prefix+"curt_dnn_remote_model"); // By Claude on 06/20/2026 if (properties.getProperty(prefix+"curt_dnn_remote_model")!=null) this.curt_dnn_remote_model=(String) properties.getProperty(prefix+"curt_dnn_remote_model"); // By Claude on 06/20/2026
if (properties.getProperty(prefix+"curt_dnn_remote_srcdir")!=null) this.curt_dnn_remote_srcdir=(String) properties.getProperty(prefix+"curt_dnn_remote_srcdir"); // By Claude on 06/20/2026 if (properties.getProperty(prefix+"curt_dnn_remote_srcdir")!=null) this.curt_dnn_remote_srcdir=(String) properties.getProperty(prefix+"curt_dnn_remote_srcdir"); // By Claude on 06/20/2026
if (properties.getProperty(prefix+"curt_dnn_local")!=null) this.curt_dnn_local=Boolean.parseBoolean(properties.getProperty(prefix+"curt_dnn_local")); // By Claude on 06/27/2026
if (properties.getProperty(prefix+"curt_dnn_local_dir")!=null) this.curt_dnn_local_dir=(String) properties.getProperty(prefix+"curt_dnn_local_dir"); // By Claude on 06/27/2026
if (properties.getProperty(prefix+"curt_dnn_l2")!=null) this.curt_dnn_l2=Boolean.parseBoolean(properties.getProperty(prefix+"curt_dnn_l2")); // By Claude on 06/22/2026 if (properties.getProperty(prefix+"curt_dnn_l2")!=null) this.curt_dnn_l2=Boolean.parseBoolean(properties.getProperty(prefix+"curt_dnn_l2")); // By Claude on 06/22/2026
if (properties.getProperty(prefix+"curt_dnn_l2_model")!=null) this.curt_dnn_l2_model=(String) properties.getProperty(prefix+"curt_dnn_l2_model"); // By Claude on 06/22/2026 if (properties.getProperty(prefix+"curt_dnn_l2_model")!=null) this.curt_dnn_l2_model=(String) properties.getProperty(prefix+"curt_dnn_l2_model"); // By Claude on 06/22/2026
...@@ -9077,6 +9089,8 @@ min_str_neib_fpn 0.35 ...@@ -9077,6 +9089,8 @@ min_str_neib_fpn 0.35
imp.curt_dnn_remote_host = this.curt_dnn_remote_host; // By Claude on 06/20/2026 imp.curt_dnn_remote_host = this.curt_dnn_remote_host; // By Claude on 06/20/2026
imp.curt_dnn_remote_model = this.curt_dnn_remote_model; // By Claude on 06/20/2026 imp.curt_dnn_remote_model = this.curt_dnn_remote_model; // By Claude on 06/20/2026
imp.curt_dnn_remote_srcdir = this.curt_dnn_remote_srcdir; // By Claude on 06/20/2026 imp.curt_dnn_remote_srcdir = this.curt_dnn_remote_srcdir; // By Claude on 06/20/2026
imp.curt_dnn_local = this.curt_dnn_local; // By Claude on 06/27/2026
imp.curt_dnn_local_dir = this.curt_dnn_local_dir; // By Claude on 06/27/2026
imp.curt_dnn_l2 = this.curt_dnn_l2; // By Claude on 06/22/2026 imp.curt_dnn_l2 = this.curt_dnn_l2; // By Claude on 06/22/2026
imp.curt_dnn_l2_model = this.curt_dnn_l2_model; // By Claude on 06/22/2026 imp.curt_dnn_l2_model = this.curt_dnn_l2_model; // By Claude on 06/22/2026
imp.curt_synth_src = this.curt_synth_src; // By Claude on 06/11/2026 imp.curt_synth_src = this.curt_synth_src; // By Claude on 06/11/2026
......
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