Skip to content

Commit 730f9eb

Browse files
feat: photo read / write preparation
1 parent ac7a646 commit 730f9eb

17 files changed

+2234
-303
lines changed

cpp/FOCV_Function.cpp

Lines changed: 329 additions & 7 deletions
Large diffs are not rendered by default.

cpp/FOCV_Object.cpp

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "FOCV_Object.hpp"
99
#include "FOCV_Storage.hpp"
1010
#include "FOCV_JsiObject.hpp"
11+
#include "jsi/TypedArray.h"
1112
#include <opencv2/opencv.hpp>
1213

1314
constexpr uint64_t hashString(const char* str, size_t length) {
@@ -99,11 +100,25 @@ jsi::Object FOCV_Object::convertToJSI(jsi::Runtime& runtime, const jsi::Value* a
99100
switch(hashString(objectType.c_str(), objectType.size())) {
100101
case hashString("mat", 3): {
101102
cv::Mat mat = FOCV_Storage::get<cv::Mat>(id);
102-
103+
// cv::Mat flat = mat.reshape(1, mat.total()*mat.channels());
104+
// std::vector<uchar> vec = mat.isContinuous() ? flat : flat.clone();
105+
//
106+
// jsi::Array valueArray(runtime, vec.size());
107+
//
108+
// for (unsigned int i = 0; i < vec.size(); i++) {
109+
// valueArray.setValueAtIndex(runtime, i, jsi::Value(vec.at(i)));
110+
// }
111+
// //TODO
112+
// mrousavy::TypedArray<mrousavy::TypedArrayKind::Uint8Array>(runtime, mat.total() * mat.channels());
113+
// getTypedArray(runtime, jsBuffer)
114+
// .as<TypedArrayKind::Uint8Array>(runtime)
115+
// .updateUnsafe(runtime, (uint8_t*)data, size);
116+
117+
103118
value.setProperty(runtime, "size", jsi::Value(mat.size));
104119
value.setProperty(runtime, "cols", jsi::Value(mat.cols));
105120
value.setProperty(runtime, "rows", jsi::Value(mat.rows));
106-
121+
value.setProperty(runtime, "data", valueArray);
107122
} break;
108123
case hashString("mat_vector", 9): {
109124
std::vector<cv::Mat> mats = FOCV_Storage::get<std::vector<cv::Mat>>(id);

example/ios/Podfile.lock

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -958,6 +958,27 @@ PODS:
958958
- ReactCommon/turbomodule/bridging
959959
- ReactCommon/turbomodule/core
960960
- Yoga
961+
- react-native-image-picker (7.1.2):
962+
- DoubleConversion
963+
- glog
964+
- hermes-engine
965+
- RCT-Folly (= 2024.01.01.00)
966+
- RCTRequired
967+
- RCTTypeSafety
968+
- React-Codegen
969+
- React-Core
970+
- React-debug
971+
- React-Fabric
972+
- React-featureflags
973+
- React-graphics
974+
- React-ImageManager
975+
- React-NativeModulesApple
976+
- React-RCTFabric
977+
- React-rendererdebug
978+
- React-utils
979+
- ReactCommon/turbomodule/bridging
980+
- ReactCommon/turbomodule/core
981+
- Yoga
961982
- react-native-skia (1.3.10):
962983
- DoubleConversion
963984
- glog
@@ -1214,6 +1235,8 @@ PODS:
12141235
- React-logger (= 0.74.4)
12151236
- React-perflogger (= 0.74.4)
12161237
- React-utils (= 0.74.4)
1238+
- RNFS (2.20.0):
1239+
- React-Core
12171240
- RNReanimated (3.14.0):
12181241
- DoubleConversion
12191242
- glog
@@ -1306,6 +1329,7 @@ DEPENDENCIES:
13061329
- React-logger (from `../node_modules/react-native/ReactCommon/logger`)
13071330
- React-Mapbuffer (from `../node_modules/react-native/ReactCommon`)
13081331
- react-native-fast-opencv (from `../..`)
1332+
- react-native-image-picker (from `../node_modules/react-native-image-picker`)
13091333
- "react-native-skia (from `../node_modules/@shopify/react-native-skia`)"
13101334
- react-native-worklets-core (from `../node_modules/react-native-worklets-core`)
13111335
- React-nativeconfig (from `../node_modules/react-native/ReactCommon`)
@@ -1331,6 +1355,7 @@ DEPENDENCIES:
13311355
- React-runtimescheduler (from `../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler`)
13321356
- React-utils (from `../node_modules/react-native/ReactCommon/react/utils`)
13331357
- ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`)
1358+
- RNFS (from `../node_modules/react-native-fs`)
13341359
- RNReanimated (from `../node_modules/react-native-reanimated`)
13351360
- vision-camera-resize-plugin (from `../node_modules/vision-camera-resize-plugin`)
13361361
- VisionCamera (from `../node_modules/react-native-vision-camera`)
@@ -1405,6 +1430,8 @@ EXTERNAL SOURCES:
14051430
:path: "../node_modules/react-native/ReactCommon"
14061431
react-native-fast-opencv:
14071432
:path: "../.."
1433+
react-native-image-picker:
1434+
:path: "../node_modules/react-native-image-picker"
14081435
react-native-skia:
14091436
:path: "../node_modules/@shopify/react-native-skia"
14101437
react-native-worklets-core:
@@ -1455,6 +1482,8 @@ EXTERNAL SOURCES:
14551482
:path: "../node_modules/react-native/ReactCommon/react/utils"
14561483
ReactCommon:
14571484
:path: "../node_modules/react-native/ReactCommon"
1485+
RNFS:
1486+
:path: "../node_modules/react-native-fs"
14581487
RNReanimated:
14591488
:path: "../node_modules/react-native-reanimated"
14601489
vision-camera-resize-plugin:
@@ -1497,6 +1526,7 @@ SPEC CHECKSUMS:
14971526
React-logger: fbfb50e2a2b1b46ee087f0a52739fadecc5e81a4
14981527
React-Mapbuffer: d39610dff659d8cf1fea485abae08bbf6f9c8279
14991528
react-native-fast-opencv: ab1ec9e61ccab56c9120d847eefddd636eea859a
1529+
react-native-image-picker: c3afe5472ef870d98a4b28415fc0b928161ee5f7
15001530
react-native-skia: 4857f8a85d0e4fc152c7b8aff4fbcc7573be1cf9
15011531
react-native-worklets-core: f51430dd07bf5343d4918d28a4bb00fe8f98b982
15021532
React-nativeconfig: 2be4363c2c4ac2b42419577774e83e4e4fd2af9f
@@ -1522,6 +1552,7 @@ SPEC CHECKSUMS:
15221552
React-runtimescheduler: 3f312d33f475467a59864e0c5ab8708461387d1c
15231553
React-utils: e8b0eac797c81c574b24f6515fec4015599b643c
15241554
ReactCommon: eebffb37a90138c6db6eb8b2d952e7e5c6bc083c
1555+
RNFS: 4ac0f0ea233904cb798630b3c077808c06931688
15251556
RNReanimated: f4ff116e33e0afc3d127f70efe928847c7c66355
15261557
SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d
15271558
vision-camera-resize-plugin: 4306d5df9bce0e603bbe6ab04337f21a606f4ad1

example/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
"@shopify/react-native-skia": "^1.3.10",
1414
"react": "18.2.0",
1515
"react-native": "0.74.4",
16+
"react-native-fs": "^2.20.0",
17+
"react-native-image-picker": "^7.1.2",
1618
"react-native-reanimated": "^3.14.0",
1719
"react-native-vision-camera": "^4.5.1",
1820
"react-native-worklets-core": "^1.3.3",

example/src/App.tsx

Lines changed: 7 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -1,116 +1,12 @@
1-
import { PaintStyle, Skia } from '@shopify/react-native-skia';
2-
import { useEffect } from 'react';
3-
import { StyleSheet, Text } from 'react-native';
4-
import {
5-
OpenCV,
6-
ObjectType,
7-
type Rect,
8-
DataTypes,
9-
ColorConversionCodes,
10-
RetrievalModes,
11-
ContourApproximationModes,
12-
} from 'react-native-fast-opencv';
13-
import {
14-
Camera,
15-
useCameraDevice,
16-
useCameraPermission,
17-
useSkiaFrameProcessor,
18-
} from 'react-native-vision-camera';
19-
import { useResizePlugin } from 'vision-camera-resize-plugin';
1+
import { ImageExample } from './ImageExample';
2+
import { VisionCameraExample } from './VisionCameraExample';
203

214
export default function App() {
22-
const device = useCameraDevice('back');
23-
const { resize } = useResizePlugin();
5+
const camera = false;
246

25-
const { hasPermission, requestPermission } = useCameraPermission();
26-
27-
useEffect(() => {
28-
requestPermission();
29-
}, [requestPermission]);
30-
31-
const paint = Skia.Paint();
32-
paint.setStyle(PaintStyle.Fill);
33-
paint.setColor(Skia.Color('lime'));
34-
35-
const frameProcessor = useSkiaFrameProcessor((frame) => {
36-
'worklet';
37-
38-
const height = frame.height / 4;
39-
const width = frame.width / 4;
40-
41-
const resized = resize(frame, {
42-
scale: {
43-
width: width,
44-
height: height,
45-
},
46-
pixelFormat: 'rgb',
47-
dataType: 'uint8',
48-
});
49-
50-
const src = OpenCV.frameBufferToMat(height, width, resized);
51-
const dst = OpenCV.createObject(ObjectType.Mat, 0, 0, DataTypes.CV_8U);
52-
53-
OpenCV.invoke('cvtColor', src, dst, ColorConversionCodes.COLOR_BGR2RGB);
54-
OpenCV.invoke('cvtColor', dst, dst, ColorConversionCodes.COLOR_BGR2HSV);
55-
56-
const lowerBound = OpenCV.createObject(ObjectType.Vec3b, 37, 120, 120);
57-
const upperBound = OpenCV.createObject(ObjectType.Vec3b, 60, 255, 255);
58-
59-
OpenCV.invoke('inRange', dst, lowerBound, upperBound, dst);
60-
61-
const channels = OpenCV.createObject(ObjectType.MatVector);
62-
OpenCV.invoke('split', dst, channels);
63-
64-
const grayChannel = OpenCV.copyObjectFromVector(channels, 0);
65-
const rectangles: Rect[] = [];
66-
67-
const contours = OpenCV.invoke(
68-
'findContours',
69-
grayChannel,
70-
RetrievalModes.RETR_TREE,
71-
ContourApproximationModes.CHAIN_APPROX_SIMPLE
72-
);
73-
74-
for (const contour of contours) {
75-
const { value: area } = OpenCV.invoke('contourArea', contour, false);
76-
77-
if (area > 3000) {
78-
const rect = OpenCV.invoke('boundingRect', contour);
79-
rectangles.push(rect);
80-
}
81-
}
82-
83-
frame.render();
84-
85-
for (const rect of rectangles) {
86-
const rectangle = OpenCV.toJSValue(rect);
87-
88-
frame.drawRect(
89-
{
90-
height: rectangle.height * 4,
91-
width: rectangle.width * 4,
92-
x: rectangle.x * 4,
93-
y: rectangle.y * 4,
94-
},
95-
paint
96-
);
97-
}
98-
99-
OpenCV.clearBuffers(); // REMEMBER TO CLEAN
100-
}, []);
101-
102-
if (!hasPermission) {
103-
return <Text>No permission</Text>;
7+
if (camera) {
8+
return <VisionCameraExample />;
1049
}
105-
if (device == null) {
106-
return <Text>No device</Text>;
107-
}
108-
return (
109-
<Camera
110-
style={StyleSheet.absoluteFill}
111-
device={device}
112-
isActive={true}
113-
frameProcessor={frameProcessor}
114-
/>
115-
);
10+
11+
return <ImageExample />;
11612
}

example/src/ImageExample.tsx

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { useState } from 'react';
2+
import { Button, Image, SafeAreaView, Text } from 'react-native';
3+
import {
4+
ColorConversionCodes,
5+
convertUint8ArrayToBase64,
6+
DataTypes,
7+
ObjectType,
8+
OpenCV,
9+
readImageAsUint8Array,
10+
} from 'react-native-fast-opencv';
11+
import { launchImageLibrary, type Asset } from 'react-native-image-picker';
12+
13+
export function ImageExample() {
14+
const [photo, setPhoto] = useState<Asset | null>(null);
15+
const [base64, setBase64] = useState<string>('');
16+
17+
const test = async () => {
18+
const result = await launchImageLibrary({ mediaType: 'photo' });
19+
console.log(result);
20+
setPhoto(result.assets?.at(0) || null);
21+
};
22+
23+
const process = async () => {
24+
if (photo && photo.uri && photo.width && photo.height) {
25+
const array = await readImageAsUint8Array(photo.uri);
26+
const src = OpenCV.frameBufferToMat(photo.height, photo.width, array);
27+
28+
const result = OpenCV.toJSValue(src);
29+
// const base64String = btoa(
30+
// String.fromCharCode(...new Uint8Array(result.data))
31+
// );
32+
console.log(array.length);
33+
}
34+
};
35+
36+
return (
37+
<SafeAreaView style={{ backgroundColor: 'white', flex: 1 }}>
38+
<Button title="Select photo" onPress={test} />
39+
<Text>
40+
URI: {photo?.uri} {photo?.height} {photo?.width}
41+
</Text>
42+
<Button title="Process" onPress={process} />
43+
{base64 && photo?.width && photo?.height && (
44+
<Image
45+
source={{ uri: 'data:image/png;base64,' + base64 }}
46+
height={300}
47+
width={300}
48+
/>
49+
)}
50+
</SafeAreaView>
51+
);
52+
}

0 commit comments

Comments
 (0)