Skip to content
16 changes: 8 additions & 8 deletions apps/computer-vision/app/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,14 @@ export default function _layout() {
headerTitleStyle: { color: ColorPalette.primary },
}}
>
<Drawer.Screen
name="index"
options={{
drawerLabel: () => null,
title: 'Main Menu',
drawerItemStyle: { display: 'none' },
}}
/>
<Drawer.Screen
name="vision_camera/index"
options={{
Expand Down Expand Up @@ -132,14 +140,6 @@ export default function _layout() {
headerTitleStyle: { color: ColorPalette.primary },
}}
/>
<Drawer.Screen
name="index"
options={{
drawerLabel: () => null,
title: 'Main Menu',
drawerItemStyle: { display: 'none' },
}}
/>
</Drawer>
</GeneratingContext>
);
Expand Down
49 changes: 48 additions & 1 deletion apps/computer-vision/app/classification/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,16 @@ import Spinner from '../../components/Spinner';
import { getImage } from '../../utils';
import {
useClassification,
EFFICIENTNET_V2_S,
EFFICIENTNET_V2_S_QUANTIZED,
ClassificationModelSources,
} from 'react-native-executorch';
import { ModelPicker, ModelOption } from '../../components/ModelPicker';

const MODELS: ModelOption<ClassificationModelSources>[] = [
{ label: 'EfficientNet V2 S Quantized', value: EFFICIENTNET_V2_S_QUANTIZED },
{ label: 'EfficientNet V2 S', value: EFFICIENTNET_V2_S },
];
import { View, StyleSheet, Image, Text, ScrollView } from 'react-native';
import { BottomBar } from '../../components/BottomBar';
import React, { useContext, useEffect, useState } from 'react';
Expand All @@ -13,6 +21,8 @@ import { StatsBar } from '../../components/StatsBar';
import ErrorBanner from '../../components/ErrorBanner';

export default function ClassificationScreen() {
const [selectedModel, setSelectedModel] =
useState<ClassificationModelSources>(EFFICIENTNET_V2_S_QUANTIZED);
const [results, setResults] = useState<{ label: string; score: number }[]>(
[]
);
Expand All @@ -21,7 +31,7 @@ export default function ClassificationScreen() {

const [error, setError] = useState<string | null>(null);

const model = useClassification({ model: EFFICIENTNET_V2_S_QUANTIZED });
const model = useClassification({ model: selectedModel });
const { setGlobalGenerating } = useContext(GeneratingContext);

useEffect(() => {
Expand Down Expand Up @@ -82,6 +92,16 @@ export default function ClassificationScreen() {
: require('../../assets/icons/executorch_logo.png')
}
/>
{!imageUri && (
<View style={styles.infoContainer}>
<Text style={styles.infoTitle}>Image Classification</Text>
<Text style={styles.infoText}>
This model analyzes an image and returns the top 10 most likely
labels with confidence scores. Use the gallery or camera icons
below to pick an image, then tap the button to run the model.
</Text>
</View>
)}
{results.length > 0 && (
<View style={styles.results}>
<Text style={styles.resultHeader}>Results Top 10</Text>
Expand All @@ -96,10 +116,21 @@ export default function ClassificationScreen() {
</View>
)}
</View>
<ModelPicker
models={MODELS}
selectedModel={selectedModel}
disabled={model.isGenerating}
onSelect={(m) => {
setSelectedModel(m);
setResults([]);
}}
/>
<StatsBar inferenceTime={inferenceTime} />
<BottomBar
handleCameraPress={handleCameraPress}
runForward={runForward}
hasImage={!!imageUri}
isGenerating={model.isGenerating}
/>
</ScreenWrapper>
);
Expand Down Expand Up @@ -141,4 +172,20 @@ const styles = StyleSheet.create({
flex: 1,
marginRight: 4,
},
infoContainer: {
alignItems: 'center',
padding: 16,
gap: 8,
},
infoTitle: {
fontSize: 18,
fontWeight: '600',
color: 'navy',
},
infoText: {
fontSize: 14,
color: '#555',
textAlign: 'center',
lineHeight: 20,
},
});
51 changes: 48 additions & 3 deletions apps/computer-vision/app/instance_segmentation/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,19 @@ export default function InstanceSegmentationScreen() {

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

if (availableInputSizes && availableInputSizes.length > 0) {
setSelectedInputSize((prev) => {
if (typeof prev === 'number' && availableInputSizes.includes(prev)) {
return prev;
}
return availableInputSizes[0];
});
return;
}

setSelectedInputSize(null);
}, [isReady, availableInputSizes]);

const handleCameraPress = async (isCamera: boolean) => {
Expand All @@ -90,14 +100,21 @@ export default function InstanceSegmentationScreen() {
const runForward = async () => {
if (!imageUri || imageSize.width === 0 || imageSize.height === 0) return;

const inputSize =
availableInputSizes &&
typeof selectedInputSize === 'number' &&
availableInputSizes.includes(selectedInputSize)
? selectedInputSize
: undefined;

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

setInferenceTime(Date.now() - start);
Expand Down Expand Up @@ -144,6 +161,16 @@ export default function InstanceSegmentationScreen() {
imageWidth={imageSize.width}
imageHeight={imageSize.height}
/>
{!imageUri && (
<View style={styles.infoContainer}>
<Text style={styles.infoTitle}>Instance Segmentation</Text>
<Text style={styles.infoText}>
This model detects individual objects and draws a precise mask
over each one. Pick an image from your gallery or take one with
your camera to get started.
</Text>
</View>
)}
</View>

{imageUri && availableInputSizes && availableInputSizes.length > 0 && (
Expand Down Expand Up @@ -215,6 +242,8 @@ export default function InstanceSegmentationScreen() {
<BottomBar
handleCameraPress={handleCameraPress}
runForward={runForward}
hasImage={!!imageUri}
isGenerating={isGenerating}
/>
</ScreenWrapper>
);
Expand Down Expand Up @@ -318,4 +347,20 @@ const styles = StyleSheet.create({
color: '#999',
fontFamily: 'Courier',
},
infoContainer: {
alignItems: 'center',
padding: 16,
gap: 8,
},
infoTitle: {
fontSize: 18,
fontWeight: '600',
color: 'navy',
},
infoText: {
fontSize: 14,
color: '#555',
textAlign: 'center',
lineHeight: 20,
},
});
30 changes: 29 additions & 1 deletion apps/computer-vision/app/object_detection/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
YOLO26X,
ObjectDetectionModelSources,
} from 'react-native-executorch';
import { View, StyleSheet, Image } from 'react-native';
import { View, StyleSheet, Image, Text } from 'react-native';
import ImageWithBboxes from '../../components/ImageWithBboxes';
import React, { useContext, useEffect, useState } from 'react';
import { GeneratingContext } from '../../context';
Expand Down Expand Up @@ -112,6 +112,16 @@ export default function ObjectDetectionScreen() {
/>
)}
</View>
{!imageUri && (
<View style={styles.infoContainer}>
<Text style={styles.infoTitle}>Object Detection</Text>
<Text style={styles.infoText}>
This model detects objects in an image and draws bounding boxes
around them with class labels and confidence scores. Pick an image
from your gallery or take one with your camera to get started.
</Text>
</View>
)}
</View>
<ModelPicker
models={MODELS}
Expand All @@ -129,6 +139,8 @@ export default function ObjectDetectionScreen() {
<BottomBar
handleCameraPress={handleCameraPress}
runForward={runForward}
hasImage={!!imageUri}
isGenerating={model.isGenerating}
/>
</ScreenWrapper>
);
Expand All @@ -149,4 +161,20 @@ const styles = StyleSheet.create({
width: '100%',
height: '100%',
},
infoContainer: {
alignItems: 'center',
padding: 16,
gap: 8,
},
infoTitle: {
fontSize: 18,
fontWeight: '600',
color: 'navy',
},
infoText: {
fontSize: 14,
color: '#555',
textAlign: 'center',
lineHeight: 20,
},
});
29 changes: 29 additions & 0 deletions apps/computer-vision/app/ocr/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,17 @@ export default function OCRScreen() {
/>
)}
</View>
{!imageUri && (
<View style={styles.infoContainer}>
<Text style={styles.infoTitle}>OCR</Text>
<Text style={styles.infoText}>
This model reads and extracts text from images, returning each
detected text region with its bounding box and confidence score.
Pick an image from your gallery or take one with your camera to
get started.
</Text>
</View>
)}
{results.length > 0 && (
<View style={styles.results}>
<Text style={styles.resultHeader}>Results</Text>
Expand Down Expand Up @@ -142,6 +153,8 @@ export default function OCRScreen() {
<BottomBar
handleCameraPress={handleCameraPress}
runForward={runForward}
hasImage={!!imageUri}
isGenerating={model.isGenerating}
/>
</ScreenWrapper>
);
Expand Down Expand Up @@ -187,4 +200,20 @@ const styles = StyleSheet.create({
flex: 1,
marginRight: 4,
},
infoContainer: {
alignItems: 'center',
padding: 16,
gap: 8,
},
infoTitle: {
fontSize: 18,
fontWeight: '600',
color: 'navy',
},
infoText: {
fontSize: 14,
color: '#555',
textAlign: 'center',
lineHeight: 20,
},
});
38 changes: 38 additions & 0 deletions apps/computer-vision/app/ocr_vertical/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,26 @@ export default function VerticalOCRScreen() {
/>
)}
</View>
{!imageUri && (
<View style={styles.infoContainer}>
<Text style={styles.infoTitle}>Vertical OCR</Text>
<Text style={styles.infoText}>
This model reads vertical text (e.g. Japanese, Korean, Chinese
columns) from images, returning each detected text region with its
bounding box and confidence score. Pick an image from your gallery
or take one with your camera to get started.
</Text>
</View>
)}
{imageUri && inferenceTime !== null && results.length === 0 && (
<View style={styles.infoContainer}>
<Text style={styles.infoTitle}>No text detected</Text>
<Text style={styles.infoText}>
The model did not find any vertical text in this image. Try an
image containing vertical Japanese, Korean, or Chinese text.
</Text>
</View>
)}
{results.length > 0 && (
<View style={styles.results}>
<Text style={styles.resultHeader}>Results</Text>
Expand All @@ -113,6 +133,8 @@ export default function VerticalOCRScreen() {
<BottomBar
handleCameraPress={handleCameraPress}
runForward={runForward}
hasImage={!!imageUri}
isGenerating={model.isGenerating}
/>
</ScreenWrapper>
);
Expand Down Expand Up @@ -158,4 +180,20 @@ const styles = StyleSheet.create({
flex: 1,
marginRight: 4,
},
infoContainer: {
alignItems: 'center',
padding: 16,
gap: 8,
},
infoTitle: {
fontSize: 18,
fontWeight: '600',
color: 'navy',
},
infoText: {
fontSize: 14,
color: '#555',
textAlign: 'center',
lineHeight: 20,
},
});
Loading
Loading