Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
64 changes: 59 additions & 5 deletions apps/bare_rn/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,64 @@ const spinnerStyles = StyleSheet.create({
},
});

function ErrorBanner({
message,
onDismiss,
}: {
message: string | null;
onDismiss: () => void;
}) {
if (!message) return null;
return (
<View style={errorBannerStyles.container}>
<Text style={errorBannerStyles.message} numberOfLines={3}>
{message}
</Text>
<TouchableOpacity
onPress={onDismiss}
style={errorBannerStyles.closeButton}
>
<Text style={errorBannerStyles.closeText}>✕</Text>
</TouchableOpacity>
</View>
);
}

const errorBannerStyles = StyleSheet.create({
container: {
backgroundColor: '#FEE2E2',
borderLeftWidth: 4,
borderLeftColor: '#EF4444',
borderRadius: 8,
marginHorizontal: 16,
marginVertical: 8,
paddingVertical: 10,
paddingLeft: 12,
paddingRight: 8,
flexDirection: 'row',
alignItems: 'center',
},
message: {
flex: 1,
color: '#991B1B',
fontSize: 14,
lineHeight: 20,
},
closeButton: {
padding: 4,
marginLeft: 8,
},
closeText: {
color: '#991B1B',
fontSize: 16,
fontWeight: '600',
},
});

function App() {
const [userInput, setUserInput] = useState('');
const [isTextInputFocused, setIsTextInputFocused] = useState(false);
const [error, setError] = useState<string | null>(null);
const textInputRef = useRef<TextInput>(null);
const scrollViewRef = useRef<ScrollView>(null);

Expand All @@ -98,9 +153,7 @@ function App() {
// } });

useEffect(() => {
if (llm.error) {
console.log('LLM error:', llm.error);
}
if (llm.error) setError(String(llm.error));
}, [llm.error]);

const sendMessage = async () => {
Expand All @@ -111,7 +164,7 @@ function App() {
try {
await llm.sendMessage(userInput);
} catch (e) {
console.error(e);
setError(e instanceof Error ? e.message : String(e));
}
};

Expand All @@ -123,11 +176,12 @@ function App() {
keyboardVerticalOffset={Platform.OS === 'ios' ? 100 : 0}
>
<Spinner
visible={!llm.isReady}
visible={!llm.isReady && !llm.error}
textContent={`Loading model ${(llm.downloadProgress * 100).toFixed(0)}%`}
/>

<SafeAreaView style={styles.content}>
<ErrorBanner message={error} onDismiss={() => setError(null)} />
{llm.messageHistory.length > 0 || llm.isGenerating ? (
<ScrollView
ref={scrollViewRef}
Expand Down
17 changes: 14 additions & 3 deletions apps/computer-vision/app/classification/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import React, { useContext, useEffect, useState } from 'react';
import { GeneratingContext } from '../../context';
import ScreenWrapper from '../../ScreenWrapper';
import { StatsBar } from '../../components/StatsBar';
import ErrorBanner from '../../components/ErrorBanner';

export default function ClassificationScreen() {
const [results, setResults] = useState<{ label: string; score: number }[]>(
Expand All @@ -18,12 +19,19 @@ export default function ClassificationScreen() {
const [imageUri, setImageUri] = useState('');
const [inferenceTime, setInferenceTime] = useState<number | null>(null);

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

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

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

useEffect(() => {
if (model.error) setError(String(model.error));
}, [model.error]);

const handleCameraPress = async (isCamera: boolean) => {
const image = await getImage(isCamera);
const uri = image?.uri;
Expand All @@ -46,21 +54,24 @@ export default function ClassificationScreen() {
.map(([label, score]) => ({ label, score: score as number }));
setResults(top10);
} catch (e) {
console.error(e);
setError(e instanceof Error ? e.message : String(e));
}
}
};

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

return (
<ScreenWrapper>
<ErrorBanner message={error} onDismiss={() => setError(null)} />

<View style={styles.imageContainer}>
<Image
style={styles.image}
Expand Down
9 changes: 8 additions & 1 deletion apps/computer-vision/app/object_detection/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ const MODELS: ModelOption<ObjectDetectionModelSources>[] = [
{ label: 'RF-DeTR Nano', value: RF_DETR_NANO },
{ label: 'SSDLite MobileNet', value: SSDLITE_320_MOBILENET_V3_LARGE },
];
import ErrorBanner from '../../components/ErrorBanner';

export default function ObjectDetectionScreen() {
const [imageUri, setImageUri] = useState('');
const [results, setResults] = useState<Detection[]>([]);
const [error, setError] = useState<string | null>(null);
const [imageDimensions, setImageDimensions] = useState<{
width: number;
height: number;
Expand All @@ -38,6 +40,10 @@ export default function ObjectDetectionScreen() {
setGlobalGenerating(model.isGenerating);
}, [model.isGenerating, setGlobalGenerating]);

useEffect(() => {
if (model.error) setError(String(model.error));
}, [model.error]);

const handleCameraPress = async (isCamera: boolean) => {
const image = await getImage(isCamera);
const uri = image?.uri;
Expand All @@ -60,7 +66,7 @@ export default function ObjectDetectionScreen() {
setInferenceTime(Date.now() - start);
setResults(output);
} catch (e) {
console.error(e);
setError(e instanceof Error ? e.message : String(e));
}
}
};
Expand All @@ -76,6 +82,7 @@ export default function ObjectDetectionScreen() {

return (
<ScreenWrapper>
<ErrorBanner message={error} onDismiss={() => setError(null)} />
<View style={styles.imageContainer}>
<View style={styles.image}>
{imageUri && imageDimensions?.width && imageDimensions?.height ? (
Expand Down
13 changes: 10 additions & 3 deletions apps/computer-vision/app/ocr/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,12 @@ const MODELS: ModelOption<OCRModelSources>[] = [
{ label: 'Japanese', value: OCR_JAPANESE },
{ label: 'Korean', value: OCR_KOREAN },
];
import ErrorBanner from '../../components/ErrorBanner';

export default function OCRScreen() {
const [imageUri, setImageUri] = useState('');
const [results, setResults] = useState<any[]>([]);
const [error, setError] = useState<string | null>(null);
const [imageDimensions, setImageDimensions] = useState<{
width: number;
height: number;
Expand All @@ -51,6 +53,10 @@ export default function OCRScreen() {
setGlobalGenerating(model.isGenerating);
}, [model.isGenerating, setGlobalGenerating]);

useEffect(() => {
if (model.error) setError(String(model.error));
}, [model.error]);

const handleCameraPress = async (isCamera: boolean) => {
const image = await getImage(isCamera);
const width = image?.width;
Expand All @@ -71,21 +77,22 @@ export default function OCRScreen() {
setInferenceTime(Date.now() - start);
setResults(output);
} catch (e) {
console.error(e);
setError(e instanceof Error ? e.message : String(e));
}
};

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

return (
<ScreenWrapper>
<ErrorBanner message={error} onDismiss={() => setError(null)} />
<View style={styles.container}>
<View style={styles.imageContainer}>
{imageUri && imageDimensions?.width && imageDimensions?.height ? (
Expand Down
22 changes: 17 additions & 5 deletions apps/computer-vision/app/ocr_vertical/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,34 @@ import React, { useContext, useEffect, useState } from 'react';
import { GeneratingContext } from '../../context';
import ScreenWrapper from '../../ScreenWrapper';
import { StatsBar } from '../../components/StatsBar';
import ErrorBanner from '../../components/ErrorBanner';

export default function VerticalOCRScree() {
export default function VerticalOCRScreen() {
const [imageUri, setImageUri] = useState('');
const [results, setResults] = useState<any[]>([]);
const [imageDimensions, setImageDimensions] = useState<{
width: number;
height: number;
}>();
const [inferenceTime, setInferenceTime] = useState<number | null>(null);

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

const model = useVerticalOCR({
model: OCR_ENGLISH,
independentCharacters: true,
});

const { setGlobalGenerating } = useContext(GeneratingContext);

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

useEffect(() => {
if (model.error) setError(String(model.error));
}, [model.error]);

const handleCameraPress = async (isCamera: boolean) => {
const image = await getImage(isCamera);
const width = image?.width;
Expand All @@ -46,14 +56,14 @@ export default function VerticalOCRScree() {
setInferenceTime(Date.now() - start);
setResults(output);
} catch (e) {
console.error(e);
setError(e instanceof Error ? e.message : String(e));
}
};

if (!model.isReady) {
if (!model.isReady && !model.error) {
return (
<Spinner
visible={!model.isReady}
visible={true}
textContent={`Loading the model ${(model.downloadProgress * 100).toFixed(0)} %`}
/>
);
Expand All @@ -62,6 +72,8 @@ export default function VerticalOCRScree() {
return (
<ScreenWrapper>
<View style={styles.container}>
<ErrorBanner message={error} onDismiss={() => setError(null)} />

<View style={styles.imageContainer}>
{imageUri && imageDimensions?.width && imageDimensions?.height ? (
<ImageWithBboxes2
Expand All @@ -87,7 +99,7 @@ export default function VerticalOCRScree() {
{results.map(({ text, score }, index) => (
<View key={index} style={styles.resultRecord}>
<Text style={styles.resultLabel}>{text}</Text>
<Text>{score.toFixed(3)}</Text>
<Text>{score?.toFixed(3)}</Text>
</View>
))}
</ScrollView>
Expand Down
15 changes: 11 additions & 4 deletions apps/computer-vision/app/semantic_segmentation/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import React, { useContext, useEffect, useState } from 'react';
import { GeneratingContext } from '../../context';
import ScreenWrapper from '../../ScreenWrapper';
import { StatsBar } from '../../components/StatsBar';
import ErrorBanner from '../../components/ErrorBanner';

const numberToColor: number[][] = [
[255, 87, 51], // 0 Red
Expand Down Expand Up @@ -69,19 +70,24 @@ export default function SemanticSegmentationScreen() {
DEEPLAB_V3_MOBILENET_V3_LARGE_QUANTIZED
);

const { isReady, isGenerating, downloadProgress, forward } =
const { isReady, isGenerating, downloadProgress, forward, error: modelError } =
useSemanticSegmentation({ model: selectedModel });

const [imageUri, setImageUri] = useState('');
const [imageSize, setImageSize] = useState({ width: 0, height: 0 });
const [segImage, setSegImage] = useState<SkImage | null>(null);
const [canvasSize, setCanvasSize] = useState({ width: 0, height: 0 });
const [inferenceTime, setInferenceTime] = useState<number | null>(null);
const [error, setError] = useState<string | null>(null);

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

useEffect(() => {
if (modelError) setError(String(modelError));
}, [modelError]);

const handleCameraPress = async (isCamera: boolean) => {
const image = await getImage(isCamera);
if (!image?.uri) return;
Expand Down Expand Up @@ -125,21 +131,22 @@ export default function SemanticSegmentationScreen() {
setSegImage(img);
setInferenceTime(Date.now() - start);
} catch (e) {
console.error(e);
setError(e instanceof Error ? e.message : String(e));
}
};

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

return (
<ScreenWrapper>
<ErrorBanner message={error} onDismiss={() => setError(null)} />
<View style={styles.imageCanvasContainer}>
<View style={styles.imageContainer}>
<Image
Expand Down
Loading
Loading