Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: V3 tensorflow plugin #1633

Draft
wants to merge 47 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
4b7fa85
tflite
mrousavy Jun 22, 2023
2648c3d
TF
mrousavy Jun 23, 2023
339f5e9
Tensorflow Plugin prototype
mrousavy Jun 23, 2023
de0dbb9
Update FrameProcessorRuntimeManager.mm
mrousavy Jun 23, 2023
966e2c7
Try calling model
mrousavy Jun 23, 2023
ff84d65
Data
mrousavy Jun 23, 2023
cb3de60
try accelerate
mrousavy Jun 23, 2023
383c7ed
Actually got it working lol wtf
mrousavy Jun 23, 2023
a1c24f4
Construct 2D array
mrousavy Jun 23, 2023
5f10c63
Add Metal and CoreML support
mrousavy Jun 26, 2023
1a2222f
Move up in scope
mrousavy Jun 26, 2023
6c3cde4
Float32
mrousavy Jun 26, 2023
fe39800
Move to `TensorflowPlugin.mm`
mrousavy Jun 26, 2023
36b6b98
free data
mrousavy Jun 26, 2023
1eff854
remove async
mrousavy Jun 26, 2023
5721263
Create buffer depending on output type
mrousavy Jun 28, 2023
fa47a85
Add support for Int8, Int16, Int32, and Float64
mrousavy Jun 28, 2023
72ef8c1
Update TensorflowPlugin.mm
mrousavy Jun 28, 2023
6d14868
Update TensorflowPlugin.mm
mrousavy Jun 28, 2023
e0b17f9
Log more
mrousavy Jun 28, 2023
bec364e
Update project.pbxproj
mrousavy Jun 29, 2023
7722b11
Convert
mrousavy Jun 29, 2023
bdf7d14
Correctly crop input frame
mrousavy Jun 29, 2023
31b9f84
Extract into HostObject
mrousavy Jun 29, 2023
4cd34a1
Loading type
mrousavy Jun 29, 2023
6635fec
Extract to `FrameResizer`
mrousavy Jun 29, 2023
963afc9
Clean
mrousavy Jun 29, 2023
697e6c8
Create `convertImageBufferToCGImage`
mrousavy Jun 29, 2023
35f4b3b
Add `inputs` and `outputs` property to Model
mrousavy Jun 30, 2023
eb2de8d
Expose delegate
mrousavy Jun 30, 2023
fe354ad
Async init Tensorflow
mrousavy Jun 30, 2023
53fac8c
Fix Result parsing
mrousavy Jun 30, 2023
de1d969
use all outputs
mrousavy Jun 30, 2023
deccdac
d
mrousavy Jun 30, 2023
01d2ecf
Pre-allocate ArrayBuffers
mrousavy Jun 30, 2023
61216f0
Try converting input to float
mrousavy Jun 30, 2023
32b8b9d
Try object detector
mrousavy Jun 30, 2023
00bb39b
Draw object bounding box
mrousavy Jun 30, 2023
978325e
Remove `_inputShape` class var
mrousavy Jun 30, 2023
ed9a336
Add `useTensorflowModel`
mrousavy Jul 3, 2023
f8e0b14
Update TensorflowPlugin.mm
mrousavy Jul 3, 2023
c6e25a5
Add `assetExts` again
mrousavy Jul 3, 2023
4dcc8e9
chore: Cleanup
mrousavy Jul 3, 2023
25b0d61
Update tsconfig.json
mrousavy Jul 3, 2023
aa6b8cf
chore: release 3.0.0-rc.3
mrousavy Jul 3, 2023
acb7e2a
Update podfile
mrousavy Jul 4, 2023
9166aa5
Drop builtInTrueDepthCamera
mrousavy Jul 13, 2023
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
6 changes: 6 additions & 0 deletions cpp/JSITypedArray.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,12 @@ TypedArray<T>::TypedArray(jsi::Runtime &runtime, std::vector<ContentType<T>> dat
update(runtime, data);
}

template <TypedArrayKind T>
TypedArray<T>::TypedArray(jsi::Runtime &runtime, ContentType<T>* dataToCopy, size_t size)
: TypedArrayBase(runtime, size, T) {
updateUnsafe(runtime, dataToCopy, size);
}

template <TypedArrayKind T>
TypedArray<T>::TypedArray(TypedArrayBase &&base) : TypedArrayBase(std::move(base)) {}

Expand Down
1 change: 1 addition & 0 deletions cpp/JSITypedArray.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ class TypedArray : public TypedArrayBase {
public:
explicit TypedArray(TypedArrayBase &&base);
TypedArray(jsi::Runtime &runtime, size_t size);
TypedArray(jsi::Runtime &runtime, ContentType<T>* dataToCopy, size_t size);
TypedArray(jsi::Runtime &runtime, std::vector<ContentType<T>> data);
TypedArray(TypedArray &&) = default;
TypedArray &operator=(TypedArray &&) = default;
Expand Down
Binary file not shown.
1 change: 1 addition & 0 deletions example/ios/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ target 'VisionCameraExample' do
)

pod 'VisionCamera', :path => '../..'
pod 'TensorFlowLiteObjC', :subspecs => ['Metal', 'CoreML']

post_install do |installer|
# https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202
Expand Down
27 changes: 24 additions & 3 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,22 @@ PODS:
- RNVectorIcons (9.2.0):
- React-Core
- SocketRocket (0.6.1)
- VisionCamera (3.0.0-rc.2):
- TensorFlowLiteC (2.12.0):
- TensorFlowLiteC/Core (= 2.12.0)
- TensorFlowLiteC/Core (2.12.0)
- TensorFlowLiteC/CoreML (2.12.0):
- TensorFlowLiteC/Core
- TensorFlowLiteC/Metal (2.12.0):
- TensorFlowLiteC/Core
- TensorFlowLiteObjC/Core (2.12.0):
- TensorFlowLiteC (= 2.12.0)
- TensorFlowLiteObjC/CoreML (2.12.0):
- TensorFlowLiteC/CoreML (= 2.12.0)
- TensorFlowLiteObjC/Core (= 2.12.0)
- TensorFlowLiteObjC/Metal (2.12.0):
- TensorFlowLiteC/Metal (= 2.12.0)
- TensorFlowLiteObjC/Core (= 2.12.0)
- VisionCamera (3.0.0-rc.3):
- React
- React-callinvoker
- React-Core
Expand Down Expand Up @@ -549,6 +564,8 @@ DEPENDENCIES:
- RNScreens (from `../node_modules/react-native-screens`)
- RNStaticSafeAreaInsets (from `../node_modules/react-native-static-safe-area-insets`)
- RNVectorIcons (from `../node_modules/react-native-vector-icons`)
- TensorFlowLiteObjC/CoreML
- TensorFlowLiteObjC/Metal
- VisionCamera (from `../..`)
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`)

Expand All @@ -557,6 +574,8 @@ SPEC REPOS:
- fmt
- libevent
- SocketRocket
- TensorFlowLiteC
- TensorFlowLiteObjC

EXTERNAL SOURCES:
boost:
Expand Down Expand Up @@ -719,9 +738,11 @@ SPEC CHECKSUMS:
RNStaticSafeAreaInsets: 055ddbf5e476321720457cdaeec0ff2ba40ec1b8
RNVectorIcons: fcc2f6cb32f5735b586e66d14103a74ce6ad61f8
SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17
VisionCamera: 0ee46c2c5dd7b8aa3cc3152ff4deda60ac58276f
TensorFlowLiteC: 20785a69299185a379ba9852b6625f00afd7984a
TensorFlowLiteObjC: 9a46a29a76661c513172cfffd3bf712b11ef25c3
VisionCamera: 1b078887679d0376be1748f30117bff2b876e073
Yoga: 65286bb6a07edce5e4fe8c90774da977ae8fc009

PODFILE CHECKSUM: ab9c06b18c63e741c04349c0fd630c6d3145081c
PODFILE CHECKSUM: b59fbebbb0e03033da45873b46690fdc1ee3d86f

COCOAPODS: 1.12.1
1 change: 1 addition & 0 deletions example/metro.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ module.exports = {
// We need to make sure that only one version is loaded for peerDependencies
// So we block them at the root, and alias them to the versions in example's node_modules
resolver: {
assetExts: ['tflite', 'png', 'jpg'],
blacklistRE: exclusionList(
modules.map(
(m) =>
Expand Down
65 changes: 39 additions & 26 deletions example/src/CameraPage.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as React from 'react';
import { useRef, useState, useMemo, useCallback } from 'react';
import { Platform, StyleSheet, Text, View } from 'react-native';
import { StyleSheet, Text, View } from 'react-native';
import { PinchGestureHandler, PinchGestureHandlerGestureEvent, TapGestureHandler } from 'react-native-gesture-handler';
import {
CameraDeviceFormat,
Expand All @@ -9,6 +9,7 @@ import {
sortFormats,
useCameraDevices,
useFrameProcessor,
useTensorflowModel,
VideoFile,
} from 'react-native-vision-camera';
import { Camera, frameRateIncluded } from 'react-native-vision-camera';
Expand All @@ -21,12 +22,10 @@ import { CaptureButton } from './views/CaptureButton';
import { PressableOpacity } from 'react-native-pressable-opacity';
import MaterialIcon from 'react-native-vector-icons/MaterialCommunityIcons';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[tsc] <7016> reported by reviewdog 🐶
Could not find a declaration file for module 'react-native-vector-icons/MaterialCommunityIcons'. '/home/runner/work/react-native-vision-camera/react-native-vision-camera/example/node_modules/react-native-vector-icons/MaterialCommunityIcons.js' implicitly has an 'any' type.

import IonIcon from 'react-native-vector-icons/Ionicons';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[tsc] <7016> reported by reviewdog 🐶
Could not find a declaration file for module 'react-native-vector-icons/Ionicons'. '/home/runner/work/react-native-vision-camera/react-native-vision-camera/example/node_modules/react-native-vector-icons/Ionicons.js' implicitly has an 'any' type.

import { examplePlugin } from './frame-processors/ExamplePlugin';
import type { Routes } from './Routes';
import type { NativeStackScreenProps } from '@react-navigation/native-stack';
import { useIsFocused } from '@react-navigation/core';
import { Skia } from '@shopify/react-native-skia';
import { FACE_SHADER } from './Shaders';
import { PaintStyle, Skia } from '@shopify/react-native-skia';

const ReanimatedCamera = Reanimated.createAnimatedComponent(Camera);
Reanimated.addWhitelistedNativeProps({
Expand Down Expand Up @@ -198,36 +197,50 @@ export function CameraPage({ navigation }: Props): React.ReactElement {
console.log('re-rendering camera page without active camera');
}

const radius = (format?.videoHeight ?? 1080) * 0.1;
const width = radius;
const height = radius;
const x = (format?.videoHeight ?? 1080) / 2 - radius / 2;
const y = (format?.videoWidth ?? 1920) / 2 - radius / 2;
const centerX = x + width / 2;
const centerY = y + height / 2;

const runtimeEffect = Skia.RuntimeEffect.Make(FACE_SHADER);
if (runtimeEffect == null) throw new Error('Shader failed to compile!');
const shaderBuilder = Skia.RuntimeShaderBuilder(runtimeEffect);
shaderBuilder.setUniform('r', [width]);
shaderBuilder.setUniform('x', [centerX]);
shaderBuilder.setUniform('y', [centerY]);
shaderBuilder.setUniform('resolution', [1920, 1080]);
const imageFilter = Skia.ImageFilter.MakeRuntimeShader(shaderBuilder, null, null);
const plugin = useTensorflowModel(require('../assets/object_detection_mobile_object_localizer_v1_1_default_1.tflite'));

if (plugin.state === 'loaded') console.log(JSON.stringify(plugin.model, null, 2));

const paint = Skia.Paint();
paint.setImageFilter(imageFilter);
paint.setStyle(PaintStyle.Stroke);
paint.setStrokeWidth(20);
paint.setColor(Skia.Color('red'));

const inputShape = plugin.model?.inputs[0]?.shape;
const inputFrameSize = {
width: inputShape?.[1] ?? 0,
height: inputShape?.[2] ?? 0,
};

const isIOS = Platform.OS === 'ios';
const frameProcessor = useFrameProcessor(
(frame) => {
'worklet';
console.log(`Width: ${frame.width}`);

if (isIOS) frame.render(paint);
else console.log('Drawing to the Frame is not yet available on Android. WIP PR');
if (plugin.state === 'loaded') {
const [boundingBoxes, classes, scores, count] = plugin.model.run(frame);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[tsc] <6133> reported by reviewdog 🐶
'classes' is declared but its value is never read.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[tsc] <6133> reported by reviewdog 🐶
'count' is declared but its value is never read.


for (let i = 0; i < scores.length; i++) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[tsc] <18048> reported by reviewdog 🐶
'scores' is possibly 'undefined'.

const confidence = scores[i] ?? 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[tsc] <18048> reported by reviewdog 🐶
'scores' is possibly 'undefined'.

if (confidence > 0.4) {
const scale = (1 / frame.width) * frame.height;

const top = boundingBoxes[i] / scale + 0.22;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[tsc] <18048> reported by reviewdog 🐶
'boundingBoxes' is possibly 'undefined'.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[tsc] <2532> reported by reviewdog 🐶
Object is possibly 'undefined'.

const left = boundingBoxes[i + 1];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[tsc] <18048> reported by reviewdog 🐶
'boundingBoxes' is possibly 'undefined'.

const bottom = boundingBoxes[i + 2] / scale + 0.22;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[tsc] <18048> reported by reviewdog 🐶
'boundingBoxes' is possibly 'undefined'.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[tsc] <2532> reported by reviewdog 🐶
Object is possibly 'undefined'.

const right = boundingBoxes[i + 3];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[tsc] <18048> reported by reviewdog 🐶
'boundingBoxes' is possibly 'undefined'.


frame.drawRect(
Skia.XYWHRect(left * frame.width, top * frame.height, (right - left) * frame.width, (bottom - top) * frame.height),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[tsc] <18048> reported by reviewdog 🐶
'left' is possibly 'undefined'.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[tsc] <18048> reported by reviewdog 🐶
'right' is possibly 'undefined'.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[tsc] <18048> reported by reviewdog 🐶
'left' is possibly 'undefined'.

paint,
);
console.log(left, top, right, bottom);
}
}
} else {
console.log(`Model state: ${plugin.state}..`);
}
},
[isIOS, paint],
[inputFrameSize.height, paint, plugin.model, plugin.state],
);

return (
Expand Down
9 changes: 1 addition & 8 deletions ios/CameraViewManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -102,14 +102,7 @@ final class CameraViewManager: RCTViewManager {
final func getAvailableCameraDevices(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
withPromise(resolve: resolve, reject: reject) {
let discoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: getAllDeviceTypes(), mediaType: .video, position: .unspecified)
let devices = discoverySession.devices.filter {
if #available(iOS 11.1, *) {
// exclude the true-depth camera. The True-Depth camera has YUV and Infrared, can't take photos!
return $0.deviceType != .builtInTrueDepthCamera
}
return true
}
return devices.map {
return discoverySession.devices.map {
return [
"id": $0.uniqueID,
"devices": $0.physicalDevices.map(\.deviceType.descriptor),
Expand Down
3 changes: 3 additions & 0 deletions ios/Frame Processor/FrameProcessorRuntimeManager.mm
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

#import "FrameProcessorUtils.h"
#import "FrameProcessorCallback.h"
#import "../Tensorflow Plugin/TensorflowPlugin.h"
#import "../React Utils/JSIUtils.h"
#import "../../cpp/JSITypedArray.h"

Expand Down Expand Up @@ -120,6 +121,8 @@ - (void) setupWorkletContext:(jsi::Runtime&)runtime {

// global.FrameProcessorPlugins Proxy
runtime.global().setProperty(runtime, "FrameProcessorPlugins", frameProcessorPlugins);

TensorflowPlugin::installToRuntime(runtime, callInvoker);

NSLog(@"FrameProcessorBindings: Frame Processor plugins installed!");
}
Expand Down
55 changes: 55 additions & 0 deletions ios/Tensorflow Plugin/FrameResizer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//
// FrameResizer.h
// VisionCamera
//
// Created by Marc Rousavy on 29.06.23.
// Copyright © 2023 mrousavy. All rights reserved.
//

#pragma once

#import <memory>
#import <Foundation/Foundation.h>
#import <Accelerate/Accelerate.h>
#import <AVFoundation/AVFoundation.h>
#import <TensorFlowLiteObjC/TFLTensorFlowLite.h>

class FrameResizer {
public:
/**
Create a new instance of the Frame Resizer. The Frame Resizer can resize incoming Frames to the given target width, height, and byte size.
It uses Accelerate to downscale, crop, and re-sample Frames to the correct pixelformat.
The constructor will allocate a few buffers used for internal steps.

`targetWidth`: The width to resize all incoming frames to
`targetHeight`: The height to resize all incoming frames to
`channels`: The number of channels to use for the output. E.g. for RGB this should be 3, for RGBA this should be 4.
`dataType`: The type of the output data as a Tensorflow type.

Examples:
- FrameResizer(192, 192, 3, TFLTensorDataTypeUInt8) will create a buffer with the size of 110.592
- FrameResizer(192, 192, 3, TFLTensorDataTypeFloat32) will create a buffer with the size of 442.368
*/
explicit FrameResizer(size_t targetWidth, size_t targetHeight, size_t channels, TFLTensorDataType dataType);
~FrameResizer();

/**
Resize the given Frame to the target dimensions and pixel formats.
*/
const vImage_Buffer& resizeFrame(CVPixelBufferRef pixelBuffer);

private:
vImage_Buffer _inputDownscaledBuffer;
vImage_Buffer _inputReformattedBuffer;

// target image with (e.g. 192)
size_t _targetWidth;
// target image height (e.g. 192)
size_t _targetHeight;
// target image bytes per row (e.g. 192*192*3)
size_t _targetBytesPerRow;
// target image channels (e.g. 3 for RGB, 4 for RGBA)
size_t _targetChannels;
// target image data type (e.g. UInt8)
TFLTensorDataType _targetDataType;
};
Loading