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
90 changes: 90 additions & 0 deletions apps/computer-vision/components/BoundingBoxes.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { Detection, LabelEnum } from 'react-native-executorch';
import { labelColor, labelColorBg } from './utils/colors';

interface Props<L extends LabelEnum> {
detections: Detection<L>[];
scaleX: number;
scaleY: number;
offsetX: number;
offsetY: number;
mirrorLabels?: boolean;
containerWidth?: number;
}

export default function BoundingBoxes<L extends LabelEnum>({
detections,
scaleX,
scaleY,
offsetX,
offsetY,
mirrorLabels = false,
containerWidth,
}: Props<L>) {
return (
<>
{detections.map((det, i) => {
const left = det.bbox.x1 * scaleX + offsetX;
const top = det.bbox.y1 * scaleY + offsetY;
const width = (det.bbox.x2 - det.bbox.x1) * scaleX;
const height = (det.bbox.y2 - det.bbox.y1) * scaleY;
const labelTop = top < 26 ? top + height + 2 : top - 26;

return (
<React.Fragment key={i}>
<View
style={[
styles.bbox,
{
left,
top,
width,
height,
borderColor: labelColor(det.label as string),
},
]}
/>
<View
style={[
styles.label,
{
left,
top: labelTop,
backgroundColor: labelColorBg(det.label as string),
...(containerWidth !== undefined && {
maxWidth: containerWidth - left,
}),
},
mirrorLabels && { transform: [{ scaleX: -1 }] },
]}
>
<Text style={styles.labelText} numberOfLines={1}>
{String(det.label)} ({(det.score * 100).toFixed(1)}%)
</Text>
</View>
</React.Fragment>
);
})}
</>
);
}

const styles = StyleSheet.create({
bbox: {
position: 'absolute',
borderWidth: 2,
borderRadius: 4,
},
label: {
position: 'absolute',
paddingHorizontal: 6,
paddingVertical: 2,
borderRadius: 4,
},
labelText: {
color: 'white',
fontSize: 11,
fontWeight: '600',
},
});
52 changes: 15 additions & 37 deletions apps/computer-vision/components/ImageWithBboxes.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import { Image, StyleSheet, View, Text } from 'react-native';
import { Image, StyleSheet, View } from 'react-native';
import { Detection } from 'react-native-executorch';
import BoundingBoxes from './BoundingBoxes';

interface Props {
imageUri: string;
Expand All @@ -21,7 +22,7 @@ export default function ImageWithBboxes({
const imageRatio = imageWidth / imageHeight;
const layoutRatio = layout.width / layout.height;

let sx, sy; // Scale in x and y directions
let sx, sy;
if (imageRatio > layoutRatio) {
// image is more "wide"
sx = layout.width / imageWidth;
Expand All @@ -35,11 +36,13 @@ export default function ImageWithBboxes({
return {
scaleX: sx,
scaleY: sy,
offsetX: (layout.width - imageWidth * sx) / 2, // Centering the image horizontally
offsetY: (layout.height - imageHeight * sy) / 2, // Centering the image vertically
offsetX: (layout.width - imageWidth * sx) / 2,
offsetY: (layout.height - imageHeight * sy) / 2,
};
};

const { scaleX, scaleY, offsetX, offsetY } = calculateAdjustedDimensions();

return (
<View
style={styles.container}
Expand All @@ -57,24 +60,14 @@ export default function ImageWithBboxes({
: require('../assets/icons/executorch_logo.png')
}
/>
{detections.map((detection, index) => {
const { scaleX, scaleY, offsetX, offsetY } =
calculateAdjustedDimensions();
const { x1, y1, x2, y2 } = detection.bbox;

const left = x1 * scaleX + offsetX;
const top = y1 * scaleY + offsetY;
const width = (x2 - x1) * scaleX;
const height = (y2 - y1) * scaleY;

return (
<View key={index} style={[styles.bbox, { left, top, width, height }]}>
<Text style={styles.label}>
{detection.label} ({(detection.score * 100).toFixed(1)}%)
</Text>
</View>
);
})}
<BoundingBoxes
detections={detections}
scaleX={scaleX}
scaleY={scaleY}
offsetX={offsetX}
offsetY={offsetY}
containerWidth={layout.width}
/>
</View>
);
}
Expand All @@ -89,19 +82,4 @@ const styles = StyleSheet.create({
width: '100%',
height: '100%',
},
bbox: {
position: 'absolute',
borderWidth: 2,
borderColor: 'red',
},
label: {
position: 'absolute',
top: -20,
left: 0,
backgroundColor: 'rgba(255, 0, 0, 0.7)',
color: 'white',
fontSize: 12,
paddingHorizontal: 4,
borderRadius: 4,
},
});
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
CocoLabelYolo,
} from 'react-native-executorch';
import { Canvas, Image as SkiaImage } from '@shopify/react-native-skia';
import { labelColor, labelColorBg } from '../utils/colors';
import { labelColor, labelColorBg } from '../../utils/colors';
import { TaskProps } from './types';
import {
buildDisplayInstances,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { StyleSheet, View } from 'react-native';
import { Frame, useFrameOutput } from 'react-native-vision-camera';
import { scheduleOnRN } from 'react-native-worklets';
import {
Expand All @@ -8,7 +8,7 @@ import {
SSDLITE_320_MOBILENET_V3_LARGE,
useObjectDetection,
} from 'react-native-executorch';
import { labelColor, labelColorBg } from '../utils/colors';
import BoundingBoxes from '../../BoundingBoxes';
import { TaskProps } from './types';

type ObjModelId = 'objectDetectionSsdlite' | 'objectDetectionRfdetr';
Expand Down Expand Up @@ -122,56 +122,14 @@ export default function ObjectDetectionTask({

return (
<View style={[StyleSheet.absoluteFill]} pointerEvents="none">
{detections.map((det, i) => {
const left = det.bbox.x1 * scale + offsetX;
const top = det.bbox.y1 * scale + offsetY;
const w = (det.bbox.x2 - det.bbox.x1) * scale;
const h = (det.bbox.y2 - det.bbox.y1) * scale;
return (
<View
key={i}
style={[
styles.bbox,
{
left,
top,
width: w,
height: h,
borderColor: labelColor(det.label),
},
]}
>
<View
style={[
styles.bboxLabel,
{ backgroundColor: labelColorBg(det.label) },
]}
>
<Text style={styles.bboxLabelText}>
{det.label} {(det.score * 100).toFixed(1)}
</Text>
</View>
</View>
);
})}
<BoundingBoxes
detections={detections}
scaleX={scale}
scaleY={scale}
offsetX={offsetX}
offsetY={offsetY}
containerWidth={canvasSize.width}
/>
</View>
);
}

const styles = StyleSheet.create({
bbox: {
position: 'absolute',
borderWidth: 2,
borderColor: 'cyan',
borderRadius: 4,
},
bboxLabel: {
position: 'absolute',
top: -22,
left: -2,
paddingHorizontal: 6,
paddingVertical: 2,
borderRadius: 4,
},
bboxLabelText: { color: 'white', fontSize: 11, fontWeight: '600' },
});
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
Skia,
SkImage,
} from '@shopify/react-native-skia';
import { CLASS_COLORS } from '../utils/colors';
import { CLASS_COLORS } from '../../utils/colors';
import { TaskProps } from './types';

type SegModelId =
Expand Down
Loading