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

interface Props {
detections: Detection[];
scaleX: number;
scaleY: number;
offsetX: number;
offsetY: number;
mirrorLabels?: boolean;
}

export default function BoundingBoxes({
detections,
scaleX,
scaleY,
offsetX,
offsetY,
mirrorLabels = false,
}: Props) {
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),
},
]}
/>
<View
style={[
styles.label,
{
left,
top: labelTop,
backgroundColor: labelColorBg(det.label),
},
mirrorLabels && { transform: [{ scaleX: -1 }] },
]}
>
<Text style={styles.labelText} numberOfLines={1}>
{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',
},
});
51 changes: 14 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,13 @@ 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}
/>
</View>
);
}
Expand All @@ -89,19 +81,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,
},
});
41 changes: 41 additions & 0 deletions apps/computer-vision/components/utils/colors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
export const CLASS_COLORS: number[][] = [
[0, 0, 0, 0],
[51, 255, 87, 180],
[51, 87, 255, 180],
[255, 51, 246, 180],
[51, 255, 246, 180],
[243, 255, 51, 180],
[141, 51, 255, 180],
[255, 131, 51, 180],
[51, 255, 131, 180],
[131, 51, 255, 180],
[255, 255, 51, 180],
[51, 255, 255, 180],
[255, 51, 143, 180],
[127, 51, 255, 180],
[51, 255, 175, 180],
[255, 175, 51, 180],
[179, 255, 51, 180],
[255, 87, 51, 180],
[255, 51, 162, 180],
[51, 162, 255, 180],
[162, 51, 255, 180],
];

export function hashLabel(label: string): number {
let hash = 5381;
for (let i = 0; i < label.length; i++) {
hash = (hash + hash * 32 + label.charCodeAt(i)) % 1000003;
}
return 1 + (Math.abs(hash) % (CLASS_COLORS.length - 1));
}

export function labelColor(label: string): string {
const color = CLASS_COLORS[hashLabel(label)]!;
return `rgba(${color[0]},${color[1]},${color[2]},1)`;
}

export function labelColorBg(label: string): string {
const color = CLASS_COLORS[hashLabel(label)]!;
return `rgba(${color[0]},${color[1]},${color[2]},0.75)`;
}
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 @@ -118,57 +118,14 @@ export default function ObjectDetectionTask({
]}
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) },
cameraPosition === 'front' && { transform: [{ scaleX: -1 }] },
]}
>
<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}
mirrorLabels={cameraPosition === 'front'}
/>
</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' },
});
42 changes: 1 addition & 41 deletions apps/computer-vision/components/vision_camera/utils/colors.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1 @@
export const CLASS_COLORS: number[][] = [
[0, 0, 0, 0],
[51, 255, 87, 180],
[51, 87, 255, 180],
[255, 51, 246, 180],
[51, 255, 246, 180],
[243, 255, 51, 180],
[141, 51, 255, 180],
[255, 131, 51, 180],
[51, 255, 131, 180],
[131, 51, 255, 180],
[255, 255, 51, 180],
[51, 255, 255, 180],
[255, 51, 143, 180],
[127, 51, 255, 180],
[51, 255, 175, 180],
[255, 175, 51, 180],
[179, 255, 51, 180],
[255, 87, 51, 180],
[255, 51, 162, 180],
[51, 162, 255, 180],
[162, 51, 255, 180],
];

export function hashLabel(label: string): number {
let hash = 5381;
for (let i = 0; i < label.length; i++) {
hash = (hash + hash * 32 + label.charCodeAt(i)) % 1000003;
}
return 1 + (Math.abs(hash) % (CLASS_COLORS.length - 1));
}

export function labelColor(label: string): string {
const color = CLASS_COLORS[hashLabel(label)]!;
return `rgba(${color[0]},${color[1]},${color[2]},1)`;
}

export function labelColorBg(label: string): string {
const color = CLASS_COLORS[hashLabel(label)]!;
return `rgba(${color[0]},${color[1]},${color[2]},0.75)`;
}
export * from '../../utils/colors';
Loading