Commit d345d6fc authored by Andrey Filippov's avatar Andrey Filippov

CLAUDE: Deploy continuous-velocity (reg) DNN head in Java (T7)

CuasDnnInfer auto-detects reg from the ONNX output channels (6 = reg head
{det,Vx,Vy,logvar,dx,dy}, else grid) via getOutCh()/isReg(), and adds
inferROIReg() returning {S,Vx,Vy,sigma,dx,dy} per ROI pixel (S=sigmoid(det),
Vx/Vy model-bounded, sigma=exp(logvar/2)). CuasDetectRT branches: reg saves an
S-first {S,Vx,Vy,sigma} hyperstack (-VXYS) + -OFFSET (showArraysHyperstack,
channel x time x ROI), metadata-tagged; ghostbuster/grid renders/recurrent
skipped (no grid corners, |v|<=vmax baked in). Set curt_dnn_patch=32 for the
reg model; curt_dnn_vmax unused in reg mode.
Co-Authored-By: 's avatarClaude Opus 4.8 (1M context) <noreply@anthropic.com>
parent 9540ac2a
......@@ -1249,6 +1249,37 @@ public class CuasDetectRT {
int [] tw = timeWindow(cand_ts); int w0 = tw[0], num = tw[1] - tw[0]; // By Claude on 06/14/2026
if (num <= 0) continue; // By Claude on 06/14/2026
double dnn_thresh = clt_parameters.imp.curt_dnn_thresh; // By Claude on 06/13/2026
if (dnn.isReg()) { // continuous-velocity (reg) head: save {S,Vx,Vy,sigma} + offset hyperstacks; no grid/ghostbuster/recurrent. By Claude on 06/17/2026
String title_reg = title_conv5d+"-DNN"+((nlev > 0)?("-LEV"+nlev):"")+"-ROI"+curt_save_select.x+"_"+curt_save_select.y+"_"+curt_save_select.width+"_"+curt_save_select.height;
int rnpr = curt_save_select.width * curt_save_select.height;
double [][][] svxys = new double [4][num][rnpr]; // [S,Vx,Vy,sigma][scene][pix] - S first (ImageJ auto-ranges from slice 1)
double [][][] soffr = new double [3][num][rnpr]; // [dx,dy,s]
String [] ts_reg = new String [num];
for (int j = 0; j < num; j++) {
int n5d = w0 + j, newest = n5d * dnn_stride + N_dnn - 1;
float [][] window = new float [N_dnn][];
for (int h = 0; h < N_dnn; h++) {
double [] src = framesD[newest - h]; float [] fw = new float [src.length];
for (int k = 0; k < src.length; k++) fw[k] = (float) src[k];
window[h] = fw;
}
double [][] o6 = dnn.inferROIReg(window, width, height, curt_save_select); // [pix]{S,Vx,Vy,sigma,dx,dy}
for (int p = 0; p < rnpr; p++) {
double sv = o6[p][0];
svxys[0][j][p] = sv; svxys[1][j][p] = o6[p][1]; svxys[2][j][p] = o6[p][2]; svxys[3][j][p] = o6[p][3];
soffr[0][j][p] = (sv >= dnn_thresh) ? o6[p][4] : Double.NaN;
soffr[1][j][p] = (sv >= dnn_thresh) ? o6[p][5] : Double.NaN;
soffr[2][j][p] = sv;
}
ts_reg[j] = ts_pyramid[nlev][newest] + " f"+newest;
System.out.println(now()+" DNN-reg scene "+(j+1)+"/"+num+" (frame "+newest+") done");
}
QuadCLTCPU.saveImagePlusInDirectory(tagCuasImp(ShowDoubleFloatArrays.showArraysHyperstack(
svxys, curt_save_select.width, title_reg+"-VXYS", ts_reg, new String[]{"S","Vx","Vy","sigma"}, false), clt_parameters.imp), getModelDirectory());
QuadCLTCPU.saveImagePlusInDirectory(tagCuasImp(ShowDoubleFloatArrays.showArraysHyperstack(
soffr, curt_save_select.width, title_reg+"-OFFSET", ts_reg, new String[]{"dx","dy","s"}, false), clt_parameters.imp), getModelDirectory());
continue; // reg level done - skip the grid path below
}
double [][][][] dnn_roi = new double [num][][][]; // By Claude on 06/13/2026
double [][][] dnn_off = new double [num][][]; // per scene: [roi_pix][{dx,dy,s}] - sub-pixel offset viz // By Claude on 06/14/2026
String [] ts_dnn = new String [num]; // By Claude on 06/13/2026
......
......@@ -32,6 +32,7 @@ public class CuasDnnInfer implements AutoCloseable {
private final String inputName;
private final int nframes;
private int patch = 24; // receptive-field / training patch (24 or 32); set via setPatch to match the model. By Claude on 06/16/2026
private int outCh = -1; // model output channels: 6 = continuous-velocity (reg) head {det,Vx,Vy,logvar,dx,dy}; else grid. By Claude on 06/17/2026
public CuasDnnInfer(String modelSpec, int nframes) throws Exception {
String local = resolveModel(modelSpec);
......@@ -50,14 +51,26 @@ public class CuasDnnInfer implements AutoCloseable {
if ((shape != null) && (shape.length >= 2) && (shape[1] > 0)) modelN = (int) shape[1];
} catch (Exception e) { /* keep the hint value */ }
this.nframes = modelN;
// Output channel count [B, out_ch, H, W]: 6 = continuous-velocity (reg) head, else grid (1+vdim^2+2). By Claude on 06/17/2026
try {
String outName = session.getOutputNames().iterator().next();
long[] osh = ((ai.onnxruntime.TensorInfo) session.getOutputInfo().get(outName).getInfo()).getShape();
if ((osh != null) && (osh.length >= 2) && (osh[1] > 0)) this.outCh = (int) osh[1];
} catch (Exception e) { /* leave -1 (grid assumed) */ }
System.out.println("CuasDnnInfer: loaded " + local + " input=" + inputName
+ " outputs=" + session.getOutputNames() + " nframes=" + this.nframes
+ " outputs=" + session.getOutputNames() + " nframes=" + this.nframes + " outCh=" + this.outCh
+ (isReg() ? " (REG head: S,Vx,Vy,sigma)" : "")
+ ((modelN != nframes) ? " (hint was " + nframes + ")" : ""));
}
/** Temporal depth the loaded model expects (N frames), read from its input shape. By Claude on 06/15/2026 */
public int getNFrames() { return nframes; }
/** Model output channel count (6 = reg head; else grid). By Claude on 06/17/2026 */
public int getOutCh() { return outCh; }
/** True if the loaded model is the continuous-velocity (reg) head: out = {det,Vx,Vy,logvar,dx,dy}. */
public boolean isReg() { return outCh == 6; }
/** Set the receptive-field / training patch size (24 or 32) the model was trained at;
* inferROI extracts this P×P patch per ROI pixel (half=P/2 = the output-pixel index). By Claude on 06/16/2026 */
public void setPatch(int p) { if (p > 0) this.patch = p; }
......@@ -140,6 +153,52 @@ public class CuasDnnInfer implements AutoCloseable {
return field;
}
/** Continuous-velocity (reg) inference over an ROI. For each pixel returns
* {S, Vx, Vy, sigma, dx, dy} from the 6-ch reg head (det, Vx, Vy, logvar, dx, dy):
* S=sigmoid(det), Vx/Vy already bounded by the model, sigma=exp(logvar/2). By Claude on 06/17/2026 */
public double[][] inferROIReg(float[][] frames, int width, int height, Rectangle roi) throws Exception {
final int N = frames.length;
final int P = patch, half = P / 2;
final int rw = roi.width, npix = rw * roi.height;
final int CHUNK = 2048;
double[][] out6 = new double[npix][6];
for (int base = 0; base < npix; base += CHUNK) {
int bs = Math.min(CHUNK, npix - base);
FloatBuffer fb = FloatBuffer.allocate(bs * N * P * P);
for (int q = 0; q < bs; q++) {
int p = base + q;
int cy = roi.y + (p / rw), cx = roi.x + (p % rw);
for (int n = 0; n < N; n++) {
float[] fr = frames[n];
for (int j = 0; j < P; j++) {
int iy = cy + j - half;
boolean yok = (iy >= 0 && iy < height);
for (int i = 0; i < P; i++) {
int ix = cx + i - half;
fb.put((yok && ix >= 0 && ix < width) ? fr[iy * width + ix] : 0f);
}
}
}
}
fb.rewind();
try (OnnxTensor in = OnnxTensor.createTensor(env, fb, new long[] {bs, N, P, P});
OrtSession.Result res = session.run(Collections.singletonMap(inputName, in))) {
float[][][][] out = (float[][][][]) res.get(0).getValue(); // [bs, 6, 1, 1]
for (int q = 0; q < bs; q++) {
float[][][] op = out[q];
double[] o = out6[base + q];
o[0] = sigmoid(op[0][0][0]); // S
o[1] = op[1][0][0]; o[2] = op[2][0][0]; // Vx, Vy (model-bounded)
o[3] = Math.exp(0.5 * op[3][0][0]); // sigma = exp(logvar/2)
o[4] = op[4][0][0]; o[5] = op[5][0][0]; // dx, dy
}
}
if ((((base / CHUNK) & 7) == 7) || (base + CHUNK >= npix))
System.out.println(" DNN inferROIReg " + Math.min(base + CHUNK, npix) + "/" + npix + " px");
}
return out6;
}
public static float sigmoid(float x) { return (float) (1.0 / (1.0 + Math.exp(-x))); }
@Override public void close() throws Exception { session.close(); }
......
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