Skip to content

Commit 9809733

Browse files
committed
refactor: remove nmsConfig, avoid making breaking changes
1 parent 68986e3 commit 9809733

8 files changed

Lines changed: 60 additions & 103 deletions

File tree

‎apps/computer-vision/app/instance_segmentation/index.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ export default function InstanceSegmentationScreen() {
112112
const start = Date.now();
113113
const output = await forward(imageUri, {
114114
confidenceThreshold: 0.5,
115-
nms: { iouThreshold: 0.55 },
115+
iouThreshold: 0.55,
116116
maxInstances: 20,
117117
returnMaskAtOriginalResolution: true,
118118
inputSize,

‎apps/computer-vision/app/segment_anything/index.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ export default function SegmentAnythingScreen() {
241241
const start = Date.now();
242242
const output = await forward(imageUri, {
243243
confidenceThreshold: 0.4,
244-
nms: { iouThreshold: 0.9 },
244+
iouThreshold: 0.9,
245245
maxInstances: 50,
246246
returnMaskAtOriginalResolution: true,
247247
});

‎apps/computer-vision/components/vision_camera/tasks/InstanceSegmentationTask.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ export default function InstanceSegmentationTask({
133133
const ih = frame.width > frame.height ? frame.width : frame.height;
134134
const result = instSegRof(frame, isFrontCamera, {
135135
confidenceThreshold: 0.5,
136-
nms: { iouThreshold: 0.5 },
136+
iouThreshold: 0.5,
137137
maxInstances: 5,
138138
returnMaskAtOriginalResolution: false,
139139
...(activeModel === 'instanceSegmentationYolo26n' && {

‎packages/react-native-executorch/src/modules/computer_vision/InstanceSegmentationModule.ts‎

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ const YOLO_SEG_CONFIG = {
3434
availableInputSizes: [384, 512, 640] as const,
3535
defaultInputSize: 384,
3636
defaultConfidenceThreshold: 0.5,
37-
defaultNms: { iouThreshold: 0.5 },
37+
defaultIouThreshold: 0.5,
3838
postprocessorConfig: {
3939
applyNMS: false,
4040
},
@@ -46,7 +46,7 @@ const FASTSAM_CONFIG = {
4646
availableInputSizes: undefined,
4747
defaultInputSize: undefined,
4848
defaultConfidenceThreshold: 0.5,
49-
defaultNms: { iouThreshold: 0.9 },
49+
defaultIouThreshold: 0.9,
5050
postprocessorConfig: {
5151
applyNMS: true,
5252
},
@@ -58,7 +58,7 @@ const RF_DETR_NANO_SEG_CONFIG = {
5858
availableInputSizes: undefined,
5959
defaultInputSize: undefined, //RFDetr exposes only one method named forward
6060
defaultConfidenceThreshold: 0.5,
61-
defaultNms: { iouThreshold: 0.5 },
61+
defaultIouThreshold: 0.5,
6262
postprocessorConfig: {
6363
applyNMS: true,
6464
},
@@ -143,7 +143,7 @@ type ResolveLabels<T extends InstanceSegmentationModelName | LabelEnum> =
143143
*
144144
* const results = await segmentation.forward('path/to/image.jpg', {
145145
* confidenceThreshold: 0.5,
146-
* nms: { iouThreshold: 0.45 },
146+
* iouThreshold: 0.45,
147147
* maxInstances: 20,
148148
* inputSize: 640,
149149
* });
@@ -237,7 +237,7 @@ export class InstanceSegmentationModule<
237237
* availableInputSizes: [640],
238238
* defaultInputSize: 640,
239239
* defaultConfidenceThreshold: 0.5,
240-
* defaultNms: { iouThreshold: 0.45 },
240+
* defaultIouThreshold: 0.45,
241241
* postprocessorConfig: { applyNMS: true },
242242
* },
243243
* );
@@ -323,10 +323,9 @@ export class InstanceSegmentationModule<
323323
const labelEnumOffset = this.labelEnumOffset;
324324
const defaultConfidenceThreshold =
325325
this.modelConfig.defaultConfidenceThreshold ?? 0.5;
326-
const defaultNmsMode = this.modelConfig.defaultNms?.mode ?? 'greedy';
327-
const defaultIouThreshold =
328-
this.modelConfig.defaultNms?.iouThreshold ?? 0.5;
326+
const defaultIouThreshold = this.modelConfig.defaultIouThreshold ?? 0.5;
329327
const defaultInputSize = this.modelConfig.defaultInputSize;
328+
const useWeightedNms = this.modelConfig.nmsMode === 'weighted';
330329

331330
return (
332331
frame: Frame,
@@ -337,9 +336,7 @@ export class InstanceSegmentationModule<
337336

338337
const confidenceThreshold =
339338
options?.confidenceThreshold ?? defaultConfidenceThreshold;
340-
const nmsMode = options?.nms?.mode ?? defaultNmsMode;
341-
const iouThreshold = options?.nms?.iouThreshold ?? defaultIouThreshold;
342-
const useWeightedNms = nmsMode === 'weighted';
339+
const iouThreshold = options?.iouThreshold ?? defaultIouThreshold;
343340
const maxInstances = options?.maxInstances ?? 100;
344341
const returnMaskAtOriginalResolution =
345342
options?.returnMaskAtOriginalResolution ?? true;
@@ -386,14 +383,14 @@ export class InstanceSegmentationModule<
386383
* 1. **String path/URI**: File path, URL, or Base64-encoded string
387384
* 2. **PixelData**: Raw pixel data from image libraries (e.g., NitroImage)
388385
* @param input - Image source (string path or PixelData object)
389-
* @param options - Optional configuration for the segmentation process. Includes `confidenceThreshold`, `nms`, `maxInstances`, `classesOfInterest`, `returnMaskAtOriginalResolution`, and `inputSize`.
386+
* @param options - Optional configuration for the segmentation process. Includes `confidenceThreshold`, `iouThreshold`, `maxInstances`, `classesOfInterest`, `returnMaskAtOriginalResolution`, and `inputSize`.
390387
* @returns A Promise resolving to an array of {@link SegmentedInstance} objects with `bbox`, `mask`, `maskWidth`, `maskHeight`, `label`, `score`.
391388
* @throws {RnExecutorchError} If the model is not loaded or if an invalid `inputSize` is provided.
392389
* @example
393390
* ```ts
394391
* const results = await segmentation.forward('path/to/image.jpg', {
395392
* confidenceThreshold: 0.6,
396-
* nms: { iouThreshold: 0.5 },
393+
* iouThreshold: 0.5,
397394
* maxInstances: 10,
398395
* inputSize: 640,
399396
* classesOfInterest: ['PERSON', 'CAR'],
@@ -420,13 +417,9 @@ export class InstanceSegmentationModule<
420417
options?.confidenceThreshold ??
421418
this.modelConfig.defaultConfidenceThreshold ??
422419
0.5;
423-
const nmsMode =
424-
options?.nms?.mode ?? this.modelConfig.defaultNms?.mode ?? 'greedy';
425420
const iouThreshold =
426-
options?.nms?.iouThreshold ??
427-
this.modelConfig.defaultNms?.iouThreshold ??
428-
0.5;
429-
const useWeightedNms = nmsMode === 'weighted';
421+
options?.iouThreshold ?? this.modelConfig.defaultIouThreshold ?? 0.5;
422+
const useWeightedNms = this.modelConfig.nmsMode === 'weighted';
430423
const maxInstances = options?.maxInstances ?? 100;
431424
const returnMaskAtOriginalResolution =
432425
options?.returnMaskAtOriginalResolution ?? true;

‎packages/react-native-executorch/src/modules/computer_vision/ObjectDetectionModule.ts‎

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ const YOLO_DETECTION_CONFIG = {
3535
availableInputSizes: [384, 512, 640] as const,
3636
defaultInputSize: 384,
3737
defaultDetectionThreshold: 0.5,
38-
defaultNms: { iouThreshold: 0.5 },
38+
defaultIouThreshold: 0.5,
3939
} satisfies ObjectDetectionConfig<typeof CocoLabelYolo>;
4040

4141
const ModelConfigs = {
@@ -45,15 +45,15 @@ const ModelConfigs = {
4545
availableInputSizes: undefined,
4646
defaultInputSize: undefined,
4747
defaultDetectionThreshold: 0.7,
48-
defaultNms: { iouThreshold: 0.55 },
48+
defaultIouThreshold: 0.55,
4949
},
5050
'rf-detr-nano': {
5151
labelMap: CocoLabel,
5252
preprocessorConfig: { normMean: IMAGENET1K_MEAN, normStd: IMAGENET1K_STD },
5353
availableInputSizes: undefined,
5454
defaultInputSize: undefined,
5555
defaultDetectionThreshold: 0.7,
56-
defaultNms: { iouThreshold: 0.55 },
56+
defaultIouThreshold: 0.55,
5757
},
5858
'yolo26n': YOLO_DETECTION_CONFIG,
5959
'yolo26s': YOLO_DETECTION_CONFIG,
@@ -66,8 +66,9 @@ const ModelConfigs = {
6666
availableInputSizes: undefined,
6767
defaultInputSize: undefined,
6868
defaultDetectionThreshold: 0.5,
69-
defaultNms: { mode: 'weighted', iouThreshold: 0.3 },
70-
defaultUseLetterbox: true,
69+
defaultIouThreshold: 0.3,
70+
nmsMode: 'weighted',
71+
useLetterbox: true,
7172
},
7273
} as const satisfies Record<
7374
ObjectDetectionModelName,
@@ -182,12 +183,11 @@ export class ObjectDetectionModule<
182183

183184
const defaultDetectionThreshold =
184185
this.modelConfig.defaultDetectionThreshold ?? 0.7;
185-
const defaultNmsMode = this.modelConfig.defaultNms?.mode ?? 'greedy';
186-
const defaultIouThreshold =
187-
this.modelConfig.defaultNms?.iouThreshold ?? 0.55;
186+
const defaultIouThreshold = this.modelConfig.defaultIouThreshold ?? 0.55;
188187
const defaultInputSize = this.modelConfig.defaultInputSize;
189188
const availableInputSizes = this.modelConfig.availableInputSizes;
190-
const defaultUseLetterbox = this.modelConfig.defaultUseLetterbox ?? false;
189+
const useWeightedNms = this.modelConfig.nmsMode === 'weighted';
190+
const useLetterbox = this.modelConfig.useLetterbox ?? false;
191191

192192
return (
193193
frame: any,
@@ -198,11 +198,8 @@ export class ObjectDetectionModule<
198198

199199
const detectionThreshold =
200200
options?.detectionThreshold ?? defaultDetectionThreshold;
201-
const nmsMode = options?.nms?.mode ?? defaultNmsMode;
202-
const iouThreshold = options?.nms?.iouThreshold ?? defaultIouThreshold;
201+
const iouThreshold = options?.iouThreshold ?? defaultIouThreshold;
203202
const inputSize = options?.inputSize ?? defaultInputSize;
204-
const useWeightedNms = nmsMode === 'weighted';
205-
const useLetterbox = options?.useLetterbox ?? defaultUseLetterbox;
206203

207204
if (
208205
availableInputSizes &&
@@ -272,16 +269,11 @@ export class ObjectDetectionModule<
272269
options?.detectionThreshold ??
273270
this.modelConfig.defaultDetectionThreshold ??
274271
0.7;
275-
const nmsMode =
276-
options?.nms?.mode ?? this.modelConfig.defaultNms?.mode ?? 'greedy';
277272
const iouThreshold =
278-
options?.nms?.iouThreshold ??
279-
this.modelConfig.defaultNms?.iouThreshold ??
280-
0.55;
273+
options?.iouThreshold ?? this.modelConfig.defaultIouThreshold ?? 0.55;
281274
const inputSize = options?.inputSize ?? this.modelConfig.defaultInputSize;
282-
const useWeightedNms = nmsMode === 'weighted';
283-
const useLetterbox =
284-
options?.useLetterbox ?? this.modelConfig.defaultUseLetterbox ?? false;
275+
const useWeightedNms = this.modelConfig.nmsMode === 'weighted';
276+
const useLetterbox = this.modelConfig.useLetterbox ?? false;
285277

286278
// Validate inputSize against availableInputSizes
287279
if (

‎packages/react-native-executorch/src/types/common.ts‎

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -146,28 +146,6 @@ export type LabelEnum = Readonly<Record<string, number | string>>;
146146
*/
147147
export type Triple<T> = readonly [T, T, T];
148148

149-
/**
150-
* Non-maximum suppression configuration shared by computer-vision models that
151-
* deduplicate overlapping detections (object detection, instance segmentation).
152-
*
153-
* - `'greedy'` (default): standard NMS — keep the highest-scoring box, drop
154-
* anything overlapping it above {@link iouThreshold}. Suits detectors like
155-
* YOLO / SSDLite whose individual anchors are trained to be accurate
156-
* standalone.
157-
* - `'weighted'`: score-weighted blending — average overlapping boxes by
158-
* their scores instead of pruning to a single winner. Required for
159-
* detectors like BlazeFace whose anchors were trained as an ensemble.
160-
*
161-
* Both fields are optional in per-call options and the model preset's
162-
* `defaultNms`; missing fields fall back through the preset default to the
163-
* runtime default.
164-
* @category Types
165-
*/
166-
export interface NmsConfig {
167-
mode?: 'greedy' | 'weighted';
168-
/** IoU threshold (0-1). Boxes overlapping above this are suppressed (greedy) or merged (weighted). */
169-
iouThreshold?: number;
170-
}
171149
/**
172150
* Represents raw pixel data in RGB format for vision models.
173151
*

‎packages/react-native-executorch/src/types/instanceSegmentation.ts‎

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,5 @@
11
import { RnExecutorchError } from '../errors/errorUtils';
2-
import {
3-
LabelEnum,
4-
NmsConfig,
5-
ResourceSource,
6-
Triple,
7-
Frame,
8-
PixelData,
9-
} from './common';
2+
import { LabelEnum, ResourceSource, Triple, Frame, PixelData } from './common';
103
import { Bbox } from './objectDetection';
114

125
/**
@@ -55,12 +48,12 @@ export interface InstanceSegmentationOptions<L extends LabelEnum> {
5548
*/
5649
confidenceThreshold?: number;
5750
/**
58-
* Override NMS mode and/or `iouThreshold` for this call. Partial — missing
59-
* fields fall back to the model preset's `defaultNms`. Ignored for models
60-
* whose preset disables external NMS (`postprocessorConfig.applyNMS: false`,
61-
* e.g. YOLO-seg which does NMS internally).
51+
* IoU threshold for non-maximum suppression (0-1). Defaults to the model
52+
* preset's `defaultIouThreshold`. Ignored for models whose preset disables
53+
* external NMS (`postprocessorConfig.applyNMS: false`, e.g. YOLO-seg which
54+
* does NMS internally).
6255
*/
63-
nms?: NmsConfig;
56+
iouThreshold?: number;
6457
/**
6558
* Maximum number of instances to return. Default: 100
6659
*/
@@ -106,11 +99,17 @@ export type InstanceSegmentationConfig<T extends LabelEnum> = {
10699
postprocessorConfig?: { applyNMS?: boolean };
107100
defaultConfidenceThreshold?: number;
108101
/**
109-
* Default NMS configuration. Overridable per-call via
110-
* {@link InstanceSegmentationOptions.nms}. Has no effect when
102+
* Default IoU threshold for non-maximum suppression (0-1). Overridable per-call
103+
* via {@link InstanceSegmentationOptions.iouThreshold}. Has no effect when
111104
* `postprocessorConfig.applyNMS` is `false`.
112105
*/
113-
defaultNms?: NmsConfig;
106+
defaultIouThreshold?: number;
107+
/**
108+
* NMS algorithm baked into the model preset. Architectural — not per-call tuneable.
109+
* - `'greedy'` (default): standard NMS, suits detectors whose anchors are independently accurate.
110+
* - `'weighted'`: score-weighted box blending, required for ensemble-trained detectors.
111+
*/
112+
nmsMode?: 'greedy' | 'weighted';
114113
} & (
115114
| {
116115
availableInputSizes: readonly number[];

‎packages/react-native-executorch/src/types/objectDetection.ts‎

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,5 @@
11
import { RnExecutorchError } from '../errors/errorUtils';
2-
import {
3-
LabelEnum,
4-
NmsConfig,
5-
Triple,
6-
ResourceSource,
7-
PixelData,
8-
Frame,
9-
} from './common';
2+
import { LabelEnum, Triple, ResourceSource, PixelData, Frame } from './common';
103
import { CocoLabel } from '../constants/commonVision';
114
export { CocoLabel };
125

@@ -47,20 +40,12 @@ export interface Detection<L extends LabelEnum = typeof CocoLabel> {
4740
export interface ObjectDetectionOptions<L extends LabelEnum> {
4841
/** Minimum confidence score for detections (0-1). Defaults to the model preset's value. */
4942
detectionThreshold?: number;
50-
/** Override NMS mode and/or `iouThreshold` for this call. Partial — missing fields fall back to the model preset's `defaultNms`. */
51-
nms?: NmsConfig;
43+
/** IoU threshold for non-maximum suppression (0-1). Defaults to the model preset's `defaultIouThreshold`. */
44+
iouThreshold?: number;
5245
/** Input size for multi-method models (e.g. 384/512/640 for YOLO). */
5346
inputSize?: number;
5447
/** Restrict output to these class labels. */
5548
classesOfInterest?: (keyof L)[];
56-
/**
57-
* If true, preprocess by aspect-preserving fit + center-pad (letterbox)
58-
* instead of a plain stretch resize to the model's input size. Required
59-
* for models trained on natural-aspect crops (e.g. BlazeFace) — a stretched
60-
* portrait frame shifts anchor positions and predicts a too-narrow box.
61-
* Defaults to the model preset's `defaultUseLetterbox`.
62-
*/
63-
useLetterbox?: boolean;
6449
}
6550

6651
/**
@@ -96,10 +81,20 @@ export type ObjectDetectionConfig<T extends LabelEnum> = {
9681
preprocessorConfig?: { normMean?: Triple<number>; normStd?: Triple<number> };
9782
/** Default detection confidence threshold (0-1). */
9883
defaultDetectionThreshold?: number;
99-
/** Default NMS configuration. Overridable per-call via {@link ObjectDetectionOptions.nms}. */
100-
defaultNms?: NmsConfig;
101-
/** Default preprocessing for this model. Overridable per-call via `useLetterbox`. */
102-
defaultUseLetterbox?: boolean;
84+
/** Default IoU threshold for non-maximum suppression (0-1). Overridable per-call via {@link ObjectDetectionOptions.iouThreshold}. */
85+
defaultIouThreshold?: number;
86+
/**
87+
* NMS algorithm baked into the model preset. Architectural — not per-call tuneable.
88+
* - `'greedy'` (default): standard NMS, suits detectors whose anchors are independently accurate (YOLO, SSDLite, RF-DETR).
89+
* - `'weighted'`: score-weighted box blending, required for ensemble-trained detectors like BlazeFace.
90+
*/
91+
nmsMode?: 'greedy' | 'weighted';
92+
/**
93+
* Whether the model expects aspect-preserving fit + center-pad (letterbox) preprocessing
94+
* instead of plain stretch resize. Architectural property of the model — not per-call tuneable.
95+
* BlazeFace requires letterbox; YOLO/SSDLite/RF-DETR do not.
96+
*/
97+
useLetterbox?: boolean;
10398
} & (
10499
| {
105100
availableInputSizes: readonly number[];

0 commit comments

Comments
 (0)