Commit 1f1d501c authored by Andrey Filippov's avatar Andrey Filippov

CLAUDE: Add filterConv5dROI() — 4D NMS on fine-velocity ROI output

CuasRTUtils: new filterConv5dROI(data, roi, athresh, rthresh)
  For each (pixel, velocity) point, finds max M over the ±1 neighbourhood
  in both pixel space (3×3) and velocity space (3×3 in vx/vy).
  Zeroes the point if M >= athresh AND val < rthresh*M (sidelobe suppression).
  Passes through unchanged when M < athresh (weak-signal linear regime).
  Uses indx_center_3d3 / indx_margins_3d3 for bit-reversed multithreaded
  pixel iteration; returns a new filtered array.

CuasDetectRT: apply filterConv5dROI() to dpixels_5d_roi_pyramid[level][n5d]
  after convolve3d(), controlled by curt_vel_thresh_en / curt_vel_athresh /
  curt_vel_rthresh, at level-0 and all pyramid levels.
Co-authored-by: 's avatarClaude <claude@elphel.com>
parent c2df58ec
......@@ -332,6 +332,13 @@ public class CuasDetectRT {
curt_save_select,
null,
min_frac_last); // final double min_frac_last)
if (curt_vel_thresh_en) {
dpixels_5d_roi_pyramid[0][n5d] = cuasRTUtils.filterConv5dROI(
dpixels_5d_roi_pyramid[0][n5d],
curt_save_select,
curt_vel_athresh,
curt_vel_rthresh);
}
}
ts_5d_lev0[n5d] = ts_pyramid[0][n5d + num_hist_5d - 1];
}
......@@ -427,6 +434,13 @@ public class CuasDetectRT {
curt_save_select,
null,
min_frac_last); // final double min_frac_last)
if (curt_vel_thresh_en) {
dpixels_5d_roi_pyramid[nlev+1][n5d] = cuasRTUtils.filterConv5dROI(
dpixels_5d_roi_pyramid[nlev+1][n5d],
curt_save_select,
curt_vel_athresh,
curt_vel_rthresh);
}
}
ts_5d_lev[n5d] = ts_pyramid[nlev+1][n5d + num_hist_5d - 1];
}
......
......@@ -1491,4 +1491,117 @@ public class CuasRTUtils {
return imp;
}
/**
* 4D non-maximum suppression on a ROI fine-velocity result.
* For each (pixel, velocity) point, find the maximum M over the ±1 neighbourhood
* in both pixel space (3×3) and velocity space (3×3 in vx/vy), then:
* if M >= athresh AND val < rthresh * M → zero the point (sidelobe suppression)
* otherwise keep val unchanged (including the weak-signal passthrough when M < athresh).
*
* Uses indx_center_3d3 / indx_margins_3d3 for bit-reversed multithreaded iteration
* (same kernel radius = 1 as the pixel neighbourhood here).
*
* @param data [roi_npix][nsub][nvel] — ROI-indexed output of convolve3d(roi)
* @param roi pixel rectangle (full-image coordinates)
* @param athresh absolute threshold: M must reach this for suppression to activate
* @param rthresh relative threshold: val/M below this → zeroed (e.g. 0.5)
* @return new filtered array, same shape as data
*/
public double [][][] filterConv5dROI(
final double [][][] data,
final Rectangle roi,
final double athresh,
final double rthresh) {
final int num_vout = kernel5d[0].length;
final int vout_dim = (int) Math.sqrt(num_vout);
final int num_sub = kernel5d[0][0].length;
final int roi_x0 = roi.x, roi_y0 = roi.y;
final int roi_x1 = roi.x + roi.width, roi_y1 = roi.y + roi.height;
final double [][][] result = new double [data.length][num_sub][num_vout];
final Thread[] threads = ImageDtt.newThreadArray();
final AtomicInteger ai = new AtomicInteger(0);
// Center pixels: all 8 pixel neighbours are within image bounds; only ROI check needed.
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() { public void run() {
for (int nPix = ai.getAndIncrement(); nPix < indx_center_3d3.length; nPix = ai.getAndIncrement()) {
int ipix = indx_center_3d3[nPix];
int px = ipix % width, py = ipix / width;
if (px < roi_x0 || px >= roi_x1 || py < roi_y0 || py >= roi_y1) continue;
int roi_pix = (py - roi_y0) * roi.width + (px - roi_x0);
for (int sub_idx = 0; sub_idx < num_sub; sub_idx++) {
for (int v_out_idx = 0; v_out_idx < num_vout; v_out_idx++) {
double val = data[roi_pix][sub_idx][v_out_idx];
int vy = v_out_idx / vout_dim, vx = v_out_idx % vout_dim;
double M = 0;
for (int dpy = -1; dpy <= 1; dpy++) {
int ny = py + dpy;
for (int dpx = -1; dpx <= 1; dpx++) {
int nx = px + dpx;
if (nx < roi_x0 || nx >= roi_x1 || ny < roi_y0 || ny >= roi_y1) continue;
int nroi = (ny - roi_y0) * roi.width + (nx - roi_x0);
for (int dvy = -1; dvy <= 1; dvy++) {
int nvy = vy + dvy;
if (nvy < 0 || nvy >= vout_dim) continue;
for (int dvx = -1; dvx <= 1; dvx++) {
int nvx = vx + dvx;
if (nvx < 0 || nvx >= vout_dim) continue;
double d = data[nroi][sub_idx][nvy * vout_dim + nvx];
if (d > M) M = d;
}
}
}
}
result[roi_pix][sub_idx][v_out_idx] = (M >= athresh && val < rthresh * M) ? 0 : val;
}
}
}
}};
}
ImageDtt.startAndJoin(threads);
// Margin pixels: additionally check image bounds before ROI bounds.
ai.set(0);
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() { public void run() {
for (int nPix = ai.getAndIncrement(); nPix < indx_margins_3d3.length; nPix = ai.getAndIncrement()) {
int ipix = indx_margins_3d3[nPix];
int px = ipix % width, py = ipix / width;
if (px < roi_x0 || px >= roi_x1 || py < roi_y0 || py >= roi_y1) continue;
int roi_pix = (py - roi_y0) * roi.width + (px - roi_x0);
for (int sub_idx = 0; sub_idx < num_sub; sub_idx++) {
for (int v_out_idx = 0; v_out_idx < num_vout; v_out_idx++) {
double val = data[roi_pix][sub_idx][v_out_idx];
int vy = v_out_idx / vout_dim, vx = v_out_idx % vout_dim;
double M = 0;
for (int dpy = -1; dpy <= 1; dpy++) {
int ny = py + dpy;
if (ny < 0 || ny >= height) continue;
for (int dpx = -1; dpx <= 1; dpx++) {
int nx = px + dpx;
if (nx < 0 || nx >= width) continue;
if (nx < roi_x0 || nx >= roi_x1 || ny < roi_y0 || ny >= roi_y1) continue;
int nroi = (ny - roi_y0) * roi.width + (nx - roi_x0);
for (int dvy = -1; dvy <= 1; dvy++) {
int nvy = vy + dvy;
if (nvy < 0 || nvy >= vout_dim) continue;
for (int dvx = -1; dvx <= 1; dvx++) {
int nvx = vx + dvx;
if (nvx < 0 || nvx >= vout_dim) continue;
double d = data[nroi][sub_idx][nvy * vout_dim + nvx];
if (d > M) M = d;
}
}
}
}
result[roi_pix][sub_idx][v_out_idx] = (M >= athresh && val < rthresh * M) ? 0 : val;
}
}
}
}};
}
ImageDtt.startAndJoin(threads);
return result;
}
}
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