Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
7c358b9
chore: Rename ImageSegmentation to SemanticSegmentation
benITo47 Feb 24, 2026
2888282
Update docs
benITo47 Feb 24, 2026
a7567a1
Upadte claude skills
benITo47 Feb 24, 2026
8b8dc32
Initial instance segmetntation
benITo47 Feb 26, 2026
5869154
Rename to BaseInstanceSegmentation
benITo47 Feb 26, 2026
fb870cb
Update instance segmentaiton demo app
benITo47 Feb 27, 2026
701e6fd
Update api reference docs
benITo47 Mar 2, 2026
957849d
Add docs and fix some typechecks
benITo47 Mar 2, 2026
a5984d4
Add method unloading
benITo47 Mar 9, 2026
eb80bb1
Update gitignore
benITo47 Mar 9, 2026
090de10
Remove type field from postprocessor config
benITo47 Mar 9, 2026
f7bd1c2
Add ImageWithMasks component
benITo47 Mar 9, 2026
52e981b
Change BaseInstanceSegmentation to accept and return string labels
benITo47 Mar 9, 2026
ed18734
Remove redundant postprocessor config type
benITo47 Mar 9, 2026
cef8007
Change typedoc links
benITo47 Mar 9, 2026
e568069
Add docs
benITo47 Mar 9, 2026
907793d
Speed up mask processing
benITo47 Mar 9, 2026
1abc8da
Return class index, tweak mask drawing performance
benITo47 Mar 10, 2026
1b78a00
Add yolo specific coco labels
benITo47 Mar 10, 2026
755348d
Fix yarn stuff
benITo47 Mar 10, 2026
f65f732
Add tests
benITo47 Mar 10, 2026
2fdbed7
Add RFDetr-segmentation model, change preprocessorConfig types
benITo47 Mar 11, 2026
8da7405
Update docs/docs/02-benchmarks/inference-time.md
benITo47 Mar 11, 2026
504c9d9
Update docs/docs/02-benchmarks/memory-usage.md
benITo47 Mar 11, 2026
9e2bea4
Restore docs
benITo47 Mar 11, 2026
4d16716
Review fixes for InstanceSegmentationModule
benITo47 Mar 11, 2026
56a6523
Change typing in useInstanceSegmentation
benITo47 Mar 11, 2026
ce9cd72
Split instance segmentation postprocessing, introduce CV utils
benITo47 Mar 11, 2026
d03e6d4
Migrate ObjectDetection to common CV utils
benITo47 Mar 11, 2026
ef46c0e
Fix links
benITo47 Mar 12, 2026
48aa04b
Break up postprocessing helpers in Instance Segmentation
benITo47 Mar 12, 2026
cd340b3
Streamline postprocessing
benITo47 Mar 12, 2026
f45c022
Change inheritance to VisionModel
benITo47 Mar 13, 2026
d86bae2
Change inheritance to VisionModel
benITo47 Mar 13, 2026
9c9618b
Add live instance segmentation
benITo47 Mar 13, 2026
abda932
Checkout upstream app
benITo47 Mar 13, 2026
94e1bd7
Fix normalizing in InstanceSegmentation
benITo47 Mar 13, 2026
2e9ce55
Add missing brackets
benITo47 Mar 13, 2026
7b6d6f1
Strengthen typing
benITo47 Mar 15, 2026
d8e4795
Remove instanceId field
benITo47 Mar 16, 2026
98a8343
Apply review suggestions
benITo47 Mar 16, 2026
71312bc
Update docs
benITo47 Mar 16, 2026
013ac1c
Migrate vector<uint8> to OwningArrayBuffer
benITo47 Mar 16, 2026
3ceadb3
Apply revies suggestions
benITo47 Mar 16, 2026
7ebdbba
Change rfdetr naming, apply suggestions
benITo47 Mar 16, 2026
139536c
Apply suggestions
benITo47 Mar 16, 2026
fa85d56
Apply suggestions
benITo47 Mar 16, 2026
2b69c66
Apply suggestions
benITo47 Mar 16, 2026
0e90871
Update docs
benITo47 Mar 16, 2026
2b4ef62
Improve tests
benITo47 Mar 17, 2026
3dd47a5
Update tests
benITo47 Mar 17, 2026
8e4d43f
Readd accidentaly removed dep
benITo47 Mar 17, 2026
e4d7a61
Revert formatting in run_test.sh
benITo47 Mar 17, 2026
b3a9b89
Remove OD_live button
benITo47 Mar 17, 2026
ab4c33c
Add Mateusz's patch
benITo47 Mar 17, 2026
bac2b69
Remove console.logs, change error type
benITo47 Mar 17, 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'
| 'instanceSegmentation_yolo26n'
| 'instanceSegmentation_rfdetr';

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: 'instanceSegmentation_yolo26n', label: 'YOLO26N Seg' },
{ id: 'instanceSegmentation_rfdetr', 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
| 'instanceSegmentation_yolo26n'
| 'instanceSegmentation_rfdetr'
}
/>
)}

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