Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
a03edb8
chore: Rename ImageSegmentation to SemanticSegmentation
benITo47 Feb 24, 2026
d210b2c
Update docs
benITo47 Feb 24, 2026
fa31fc3
Upadte claude skills
benITo47 Feb 24, 2026
c7877aa
Initial instance segmetntation
benITo47 Feb 26, 2026
544f539
Rename to BaseInstanceSegmentation
benITo47 Feb 26, 2026
93f5ae3
Update instance segmentaiton demo app
benITo47 Feb 27, 2026
eaa2abb
Update api reference docs
benITo47 Mar 2, 2026
8d01aa8
Add docs and fix some typechecks
benITo47 Mar 2, 2026
8353377
Add method unloading
benITo47 Mar 9, 2026
40e9ccf
Update gitignore
benITo47 Mar 9, 2026
ec227f9
Remove type field from postprocessor config
benITo47 Mar 9, 2026
13c0f95
Add ImageWithMasks component
benITo47 Mar 9, 2026
489bfb6
Change BaseInstanceSegmentation to accept and return string labels
benITo47 Mar 9, 2026
4c62b97
Remove redundant postprocessor config type
benITo47 Mar 9, 2026
f1a31fc
Change typedoc links
benITo47 Mar 9, 2026
85f0e71
Add docs
benITo47 Mar 9, 2026
f6c4207
Speed up mask processing
benITo47 Mar 9, 2026
f81cfc7
Return class index, tweak mask drawing performance
benITo47 Mar 10, 2026
67e804f
Add yolo specific coco labels
benITo47 Mar 10, 2026
27353d2
Fix yarn stuff
benITo47 Mar 10, 2026
b72dae4
Add tests
benITo47 Mar 10, 2026
5da2857
Add RFDetr-segmentation model, change preprocessorConfig types
benITo47 Mar 11, 2026
5dbbd4d
Update docs/docs/02-benchmarks/inference-time.md
benITo47 Mar 11, 2026
3178c6d
Update docs/docs/02-benchmarks/memory-usage.md
benITo47 Mar 11, 2026
a3e3522
Restore docs
benITo47 Mar 11, 2026
8e29394
Review fixes for InstanceSegmentationModule
benITo47 Mar 11, 2026
572fb60
Change typing in useInstanceSegmentation
benITo47 Mar 11, 2026
b4ed649
Split instance segmentation postprocessing, introduce CV utils
benITo47 Mar 11, 2026
1e647e4
Migrate ObjectDetection to common CV utils
benITo47 Mar 11, 2026
8bb3899
Fix links
benITo47 Mar 12, 2026
42e71e4
Break up postprocessing helpers in Instance Segmentation
benITo47 Mar 12, 2026
6d12f4b
Streamline postprocessing
benITo47 Mar 12, 2026
5bd7929
Change inheritance to VisionModel
benITo47 Mar 13, 2026
a84c610
Change inheritance to VisionModel
benITo47 Mar 13, 2026
e4f3ff0
Add live instance segmentation
benITo47 Mar 13, 2026
3453dde
Checkout upstream app
benITo47 Mar 13, 2026
9c2707d
Fix normalizing in InstanceSegmentation
benITo47 Mar 13, 2026
f4e07ec
Add missing brackets
benITo47 Mar 13, 2026
6f986f2
Strengthen typing
benITo47 Mar 15, 2026
0a71e41
Remove instanceId field
benITo47 Mar 16, 2026
9f74e90
Apply review suggestions
benITo47 Mar 16, 2026
b9438f7
Update docs
benITo47 Mar 16, 2026
5d1ed9f
Migrate vector<uint8> to OwningArrayBuffer
benITo47 Mar 16, 2026
39f3489
Apply revies suggestions
benITo47 Mar 16, 2026
2c9b69c
Change rfdetr naming, apply suggestions
benITo47 Mar 16, 2026
2d0e616
Apply suggestions
benITo47 Mar 16, 2026
406271b
Apply suggestions
benITo47 Mar 16, 2026
7b876df
Apply suggestions
benITo47 Mar 16, 2026
b71c041
Update docs
benITo47 Mar 16, 2026
058381a
Improve tests
benITo47 Mar 17, 2026
e7fa1a3
Update tests
benITo47 Mar 17, 2026
377728f
Readd accidentaly removed dep
benITo47 Mar 17, 2026
e157b4b
Revert formatting in run_test.sh
benITo47 Mar 17, 2026
fc681bf
Remove OD_live button
benITo47 Mar 17, 2026
d6a8637
Add Mateusz's patch
benITo47 Mar 17, 2026
e07cdab
Remove console.logs, change error type
benITo47 Mar 17, 2026
6255cf0
Fix docs and class filter
benITo47 Mar 18, 2026
ab997d8
Add benchmarks for RFDETR
benITo47 Mar 19, 2026
1600daf
Fix yarn issues
benITo47 Mar 19, 2026
800d3b0
Fix live task bug
benITo47 Mar 19, 2026
ab3c5cd
fix: type declaration order
benITo47 Mar 19, 2026
be20450
Fix declaration order
benITo47 Mar 19, 2026
b50b4fb
Stylish improvements
benITo47 Mar 19, 2026
18599af
Fix declaration order
benITo47 Mar 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,4 @@ packages/react-native-executorch/common/rnexecutorch/tests/integration/assets/mo
*.tgz
Makefile
*.pte

8 changes: 8 additions & 0 deletions apps/computer-vision/app/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,14 @@ export default function _layout() {
headerTitleStyle: { color: ColorPalette.primary },
}}
/>
<Drawer.Screen
name="instance_segmentation/index"
options={{
drawerLabel: 'Instance Segmentation',
title: 'Instance Segmentation',
headerTitleStyle: { color: ColorPalette.primary },
}}
/>
<Drawer.Screen
name="object_detection/index"
options={{
Expand Down
6 changes: 6 additions & 0 deletions apps/computer-vision/app/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ export default function Home() {
>
<Text style={styles.buttonText}>Object Detection</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.button}
onPress={() => router.navigate('instance_segmentation/')}
>
<Text style={styles.buttonText}>Instance Segmentation</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.button}
onPress={() => router.navigate('ocr/')}
Expand Down
277 changes: 277 additions & 0 deletions apps/computer-vision/app/instance_segmentation/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,277 @@
import Spinner from '../../components/Spinner';
import { BottomBar } from '../../components/BottomBar';
import { getImage } from '../../utils';
import { useInstanceSegmentation, YOLO26N_SEG } from 'react-native-executorch';
import {
View,
StyleSheet,
ScrollView,
Text,
TouchableOpacity,
} from 'react-native';
import React, { useContext, useEffect, useState } from 'react';
import { GeneratingContext } from '../../context';
import ScreenWrapper from '../../ScreenWrapper';
import ImageWithMasks, {
buildDisplayInstances,
DisplayInstance,
} from '../../components/ImageWithMasks';

export default function InstanceSegmentationScreen() {
const { setGlobalGenerating } = useContext(GeneratingContext);

const {
isReady,
isGenerating,
downloadProgress,
forward,
error,
getAvailableInputSizes,
} = useInstanceSegmentation({
model: YOLO26N_SEG,
});

const [imageUri, setImageUri] = useState('');
const [imageSize, setImageSize] = useState({ width: 0, height: 0 });
const [instances, setInstances] = useState<DisplayInstance[]>([]);
const [selectedInputSize, setSelectedInputSize] = useState<number | null>(
null
);

const availableInputSizes = getAvailableInputSizes();

useEffect(() => {
setGlobalGenerating(isGenerating);
}, [isGenerating, setGlobalGenerating]);

// Set default input size when model is ready
useEffect(() => {
if (isReady && availableInputSizes && availableInputSizes.length > 0) {
setSelectedInputSize(availableInputSizes[0]);
}
}, [isReady, availableInputSizes]);

const handleCameraPress = async (isCamera: boolean) => {
const image = await getImage(isCamera);
if (!image?.uri) return;
setImageUri(image.uri);
setImageSize({
width: image.width ?? 0,
height: image.height ?? 0,
});
setInstances([]);
};

const runForward = async () => {
if (!imageUri || imageSize.width === 0 || imageSize.height === 0) return;

try {
const output = await forward(imageUri, {
confidenceThreshold: 0.5,
iouThreshold: 0.55,
maxInstances: 20,
returnMaskAtOriginalResolution: true,
inputSize: selectedInputSize ?? undefined,
});

// Convert raw masks → small Skia images immediately.
// Raw Uint8Array mask buffers (backed by native OwningArrayBuffer)
// go out of scope here and become eligible for GC right away.
setInstances(buildDisplayInstances(output));
} catch (e) {
console.error(e);
}
};

if (!isReady && error) {
return (
<ScreenWrapper>
<View style={styles.errorContainer}>
<Text style={styles.errorTitle}>Error Loading Model</Text>
<Text style={styles.errorText}>
{error?.message || 'Unknown error occurred'}
</Text>
<Text style={styles.errorCode}>Code: {error?.code || 'N/A'}</Text>
</View>
</ScreenWrapper>
);
}

if (!isReady) {
return (
<Spinner
visible={!isReady}
textContent={`Loading the model ${(downloadProgress * 100).toFixed(0)} %`}
/>
);
}

return (
<ScreenWrapper>
<View style={styles.container}>
<View style={styles.imageContainer}>
<ImageWithMasks
imageUri={imageUri}
instances={instances}
imageWidth={imageSize.width}
imageHeight={imageSize.height}
/>
</View>

{imageUri && availableInputSizes && availableInputSizes.length > 0 && (
<View style={styles.inputSizeContainer}>
<Text style={styles.inputSizeLabel}>Input Size:</Text>
<ScrollView
horizontal
showsHorizontalScrollIndicator={false}
style={styles.inputSizeScroll}
>
{availableInputSizes.map((size) => (
<TouchableOpacity
key={size}
style={[
styles.sizeButton,
selectedInputSize === size && styles.sizeButtonActive,
]}
onPress={() => setSelectedInputSize(size)}
>
<Text
style={[
styles.sizeButtonText,
selectedInputSize === size && styles.sizeButtonTextActive,
]}
>
{size}
</Text>
</TouchableOpacity>
))}
</ScrollView>
</View>
)}

{instances.length > 0 && (
<View style={styles.resultsContainer}>
<Text style={styles.resultsHeader}>
Detected {instances.length} instance(s)
</Text>
<ScrollView style={styles.resultsList}>
{instances.map((instance, idx) => (
<View key={idx} style={styles.resultRow}>
<Text style={styles.resultText}>
{instance.label || 'Unknown'} (
{(instance.score * 100).toFixed(1)}%)
</Text>
</View>
))}
</ScrollView>
</View>
)}
</View>

<BottomBar
handleCameraPress={handleCameraPress}
runForward={runForward}
/>
</ScreenWrapper>
);
}

const styles = StyleSheet.create({
container: {
flex: 6,
width: '100%',
},
imageContainer: {
flex: 1,
width: '100%',
padding: 16,
},
inputSizeContainer: {
paddingHorizontal: 16,
paddingVertical: 12,
backgroundColor: '#fff',
borderTopWidth: 1,
borderTopColor: '#e0e0e0',
},
inputSizeLabel: {
fontSize: 14,
fontWeight: '600',
color: '#333',
marginBottom: 8,
},
inputSizeScroll: {
flexDirection: 'row',
},
sizeButton: {
paddingHorizontal: 16,
paddingVertical: 8,
marginRight: 8,
borderRadius: 6,
backgroundColor: '#f0f0f0',
},
sizeButtonActive: {
backgroundColor: '#007AFF',
},
sizeButtonText: {
fontSize: 14,
fontWeight: '600',
color: '#666',
},
sizeButtonTextActive: {
color: '#fff',
},
resultsContainer: {
maxHeight: 200,
paddingHorizontal: 16,
paddingVertical: 12,
backgroundColor: '#fff',
borderTopWidth: 1,
borderTopColor: '#e0e0e0',
},
resultsHeader: {
fontSize: 16,
fontWeight: '600',
marginBottom: 8,
color: '#333',
},
resultsList: {
flex: 1,
},
resultRow: {
flexDirection: 'row',
alignItems: 'center',
paddingVertical: 6,
paddingHorizontal: 8,
marginBottom: 4,
backgroundColor: '#f9f9f9',
borderRadius: 6,
},
resultText: {
fontSize: 14,
fontWeight: '500',
color: '#333',
},
errorContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 32,
},
errorTitle: {
fontSize: 20,
fontWeight: '700',
color: '#e74c3c',
marginBottom: 12,
},
errorText: {
fontSize: 14,
color: '#555',
textAlign: 'center',
marginBottom: 8,
},
errorCode: {
fontSize: 12,
color: '#999',
fontFamily: 'Courier',
},
});
29 changes: 27 additions & 2 deletions apps/computer-vision/app/vision_camera/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,13 @@ import ColorPalette from '../../colors';
import ClassificationTask from '../../components/vision_camera/tasks/ClassificationTask';
import ObjectDetectionTask from '../../components/vision_camera/tasks/ObjectDetectionTask';
import SegmentationTask from '../../components/vision_camera/tasks/SegmentationTask';
import InstanceSegmentationTask from '../../components/vision_camera/tasks/InstanceSegmentationTask';

type TaskId = 'classification' | 'objectDetection' | 'segmentation';
type TaskId =
| 'classification'
| 'objectDetection'
| 'segmentation'
| 'instanceSegmentation';
type ModelId =
| 'classification'
| 'objectDetectionSsdlite'
Expand All @@ -43,7 +48,9 @@ type ModelId =
| 'segmentationLraspp'
| 'segmentationFcnResnet50'
| 'segmentationFcnResnet101'
| 'segmentationSelfie';
| 'segmentationSelfie'
| 'instanceSegmentationYolo26n'
| 'instanceSegmentationRfdetr';

type TaskVariant = { id: ModelId; label: string };
type Task = { id: TaskId; label: string; variants: TaskVariant[] };
Expand All @@ -67,6 +74,14 @@ const TASKS: Task[] = [
{ id: 'segmentationSelfie', label: 'Selfie' },
],
},
{
id: 'instanceSegmentation',
label: 'Inst Seg',
variants: [
{ id: 'instanceSegmentationYolo26n', label: 'YOLO26N Seg' },
{ id: 'instanceSegmentationRfdetr', label: 'RF-DETR Nano Seg' },
],
},
{
id: 'objectDetection',
label: 'Detect',
Expand Down Expand Up @@ -220,6 +235,16 @@ export default function VisionCameraScreen() {
}
/>
)}
{activeTask === 'instanceSegmentation' && (
<InstanceSegmentationTask
{...taskProps}
activeModel={
activeModel as
| 'instanceSegmentationYolo26n'
| 'instanceSegmentationRfdetr'
}
/>
)}

{!isReady && (
<View style={styles.loadingOverlay}>
Expand Down
Loading
Loading