diff --git a/apps/computer-vision/components/BoundingBoxes.tsx b/apps/computer-vision/components/BoundingBoxes.tsx new file mode 100644 index 0000000000..0c1e41c261 --- /dev/null +++ b/apps/computer-vision/components/BoundingBoxes.tsx @@ -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 { + detections: Detection[]; + scaleX: number; + scaleY: number; + offsetX: number; + offsetY: number; + mirrorLabels?: boolean; + containerWidth?: number; +} + +export default function BoundingBoxes({ + detections, + scaleX, + scaleY, + offsetX, + offsetY, + mirrorLabels = false, + containerWidth, +}: 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 ( + + + + + {String(det.label)} ({(det.score * 100).toFixed(1)}%) + + + + ); + })} + + ); +} + +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', + }, +}); diff --git a/apps/computer-vision/components/ImageWithBboxes.tsx b/apps/computer-vision/components/ImageWithBboxes.tsx index 65d345d6cb..9b13f314c6 100644 --- a/apps/computer-vision/components/ImageWithBboxes.tsx +++ b/apps/computer-vision/components/ImageWithBboxes.tsx @@ -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; @@ -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; @@ -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 ( - {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 ( - - - {detection.label} ({(detection.score * 100).toFixed(1)}%) - - - ); - })} + ); } @@ -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, - }, }); diff --git a/apps/computer-vision/components/vision_camera/utils/colors.ts b/apps/computer-vision/components/utils/colors.ts similarity index 100% rename from apps/computer-vision/components/vision_camera/utils/colors.ts rename to apps/computer-vision/components/utils/colors.ts diff --git a/apps/computer-vision/components/vision_camera/tasks/InstanceSegmentationTask.tsx b/apps/computer-vision/components/vision_camera/tasks/InstanceSegmentationTask.tsx index 829dfb2abe..e68b515946 100644 --- a/apps/computer-vision/components/vision_camera/tasks/InstanceSegmentationTask.tsx +++ b/apps/computer-vision/components/vision_camera/tasks/InstanceSegmentationTask.tsx @@ -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, diff --git a/apps/computer-vision/components/vision_camera/tasks/ObjectDetectionTask.tsx b/apps/computer-vision/components/vision_camera/tasks/ObjectDetectionTask.tsx index 0ac5ef4af4..deb5368c90 100644 --- a/apps/computer-vision/components/vision_camera/tasks/ObjectDetectionTask.tsx +++ b/apps/computer-vision/components/vision_camera/tasks/ObjectDetectionTask.tsx @@ -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 { @@ -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'; @@ -122,56 +122,14 @@ export default function ObjectDetectionTask({ return ( - {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 ( - - - - {det.label} {(det.score * 100).toFixed(1)} - - - - ); - })} + ); } - -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' }, -}); diff --git a/apps/computer-vision/components/vision_camera/tasks/SegmentationTask.tsx b/apps/computer-vision/components/vision_camera/tasks/SegmentationTask.tsx index 5bdd33b8f1..31dcea51da 100644 --- a/apps/computer-vision/components/vision_camera/tasks/SegmentationTask.tsx +++ b/apps/computer-vision/components/vision_camera/tasks/SegmentationTask.tsx @@ -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 =