regionsOfInterestPrune#
Executive Summary#
RegionsOfInterestPrune consumes the row/column above-threshold pixel sums produced by
fpgaImagePipeline and outputs up to ROI_CANDIDATES_MAX bounding-box candidates sorted
by estimated pixel count. It sits immediately downstream of fpgaImagePipeline and feeds
downstream tracking or centroiding stages.
The module is split into two classes:
RegionsOfInterestPruneAlgorithm— pure C++, no Basilisk dependencies. Accepts rawuint16_trow/col sum arrays and returns aRoiCandidatesMsgPayload.RegionsOfInterestPrune— thin Basilisk adapter. ReadsFpgaRowColSumMsgPayload, delegates computation toRegionsOfInterestPruneAlgorithm, and writes the result tocandidatesOutMsg.
Pipeline stages:
rowColSumInMsg
│
▼
1. Find spans ── contiguous non-zero runs in rowSums / colSums; accumulate per-span sums
│
▼
2. Pre-filter ── top maxRowSpans row spans, top maxColSpans col spans (by accumulator)
│
▼
3. Cross-product ─ bounding boxes; count = min(R[k], C[l]) per box
│
▼
4. Sort & truncate sort by count desc, keep ≤ ROI_CANDIDATES_MAX
│
▼
candidatesOutMsg
Algorithm Details#
Step 1 — span detection and accumulation
A span is a maximal contiguous run of non-zero entries in a 1-D sum array. Each row span
(r, h) and col span (c, w) defines the start and length of a group of image rows or
columns that contain at least one above-threshold pixel. The per-span accumulator sum R[k]
(or C[l]) is computed in the same forward pass as span detection.
Step 2 — pre-filter safeguard
In the worst case the cross-product of all row spans and all col spans is unbounded. To cap
computation, the module keeps only the top maxRowSpans row spans (by their row-sum
accumulator R[k]) and the top maxColSpans col spans (by C[l]). The cross-product
is then at most maxRowSpans × maxColSpans candidates regardless of image size.
Because the true rank-1 candidate lies at the intersection of argmax(R) and argmax(C),
it is always preserved by this filter.
Step 3 — cross-product and pixel count estimation
Every combination of a filtered row span k and a filtered col span l defines a
bounding box candidate (r, h, c, w) where r / h are the start row and height from
span k, and c / w are the start column and width from span l. The estimated
pixel count for each box is:
R[k]= sum ofrowSumsover row spank— overcounts pixels outside col spanlC[l]= sum ofcolSumsover col spanl— overcounts pixels outside row spankcount = min(R[k], C[l])— tightest upper bound obtainable from 1-D projections alone
Step 4 — sort and truncate
All candidates from Step 3 are sorted by count in descending order. If the number of
candidates exceeds ROI_CANDIDATES_MAX the tail is dropped. The final list is packed into
RoiCandidatesMsgPayload with candidates[0] as rank-1 (highest count).
Message Connection Descriptions#
Msg Variable Name |
Msg Type |
Description |
|---|---|---|
rowColSumInMsg |
Per-row and per-column above-threshold pixel counts from |
|
candidatesOutMsg |
RoiCandidatesMsgPayload |
Up to |
User Guide#
Import the module:
from xmera.fswAlgorithms import regionsOfInterestPrune
Instantiate and configure:
pruner = regionsOfInterestPrune.RegionsOfInterestPrune() pruner.ModelTag = "roiPrune" # Optional: tune the pre-filter safeguard (defaults: 3 each) pruner.setMaxRowSpans(10) pruner.setMaxColSpans(10)
Connect to the upstream pipeline:
pruner.rowColSumInMsg.subscribeTo(pipeline.rowColSumOutMsg)
Add to simulation task:
sim.AddModelToTask(taskName, pruner)
Read results:
n = pruner.getNumCandidates() # number of candidates returned (≤ ROI_CANDIDATES_MAX) row = pruner.getCandidateRow(0) # top-ranked candidate col = pruner.getCandidateCol(0) h = pruner.getCandidateHeight(0) w = pruner.getCandidateWidth(0) count = pruner.getCandidateCount(0) # estimated above-threshold pixel count
Unit Tests#
All tests reside in _UnitTest/test_regionsOfInterestPrune.py.
Test |
Description |
|---|---|
|
Verifies |
|
Single 5×5 bright block (M=1): checks exactly one candidate is output with count=1. |
|
13×13 and 5×5 blocks sharing a col span: verifies rank-1 and rank-2 bounding boxes and positions. |
|
Three col-aligned blocks (13×13, 9×9, 5×5): confirms candidates are sorted by estimated count (81, 25, 1) so rank-2 is the 9×9 block, not the 5×5 block. |
|
End-to-end integration on
|
Class RegionsOfInterestPrune#
-
class RegionsOfInterestPrune : public SysModel#
Basilisk adapter for the regions-of-interest pruning module.
Reads FpgaRowColSumMsgPayload, delegates computation to RegionsOfInterestPruneAlgorithm, and publishes up to MAX_NUMBER_REGIONS candidates sorted by estimated above-threshold pixel count to regionsIdentifiedOutMsg (RegionsIdentifiedMsgPayload).
Public Members
-
ReadFunctor<FpgaRowColSumMsgPayload> rowColSumInMsg#
Row/col accumulators from fpgaImagePipeline.
-
ReadFunctor<FpgaThreshImageMsgPayload> threshImageInMsg#
Optional: threshold image for visualization.
-
Message<RegionsIdentifiedMsgPayload> regionsIdentifiedOutMsg#
Pruned candidates as RegionsIdentifiedMsg.
-
ReadFunctor<FpgaRowColSumMsgPayload> rowColSumInMsg#