Skip to content

Commit 9c51f12

Browse files
fix: wip color and format conversion
1 parent a309564 commit 9c51f12

File tree

7 files changed

+124
-60
lines changed

7 files changed

+124
-60
lines changed

cpp/FOCV_Object.cpp

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ constexpr uint64_t hashString(const char* str, size_t length) {
2929
return hash;
3030
}
3131

32-
jsi::Object FOCV_Object::create(jsi::Runtime& runtime, const jsi::Value* arguments) {
32+
jsi::Object FOCV_Object::create(jsi::Runtime& runtime, const jsi::Value* arguments, size_t argCount) {
3333
std::string id = "";
3434
std::string objectType = arguments[0].asString(runtime).utf8(runtime);
3535

@@ -38,29 +38,27 @@ jsi::Object FOCV_Object::create(jsi::Runtime& runtime, const jsi::Value* argumen
3838
int rows = arguments[1].asNumber();
3939
int cols = arguments[2].asNumber();
4040
int type = arguments[3].asNumber();
41-
42-
if(arguments[4].isObject()) {
43-
auto rawArray = arguments[4].asObject(runtime);
44-
auto array = rawArray.asArray(runtime);
45-
46-
auto rawLength = rawArray.getProperty(runtime, "length");
47-
auto length = rawLength.asNumber();
48-
49-
std::vector<float> vec;
50-
51-
for(auto i = 0; i < length; i++) {
52-
vec.push_back(array.getValueAtIndex(runtime, i).asNumber());
53-
}
54-
55-
cv::Mat mat{vec, true};
56-
mat = mat.reshape(1, rows);
57-
mat.convertTo(mat, type);
58-
59-
id = FOCV_Storage::save(mat);
60-
} else {
61-
cv::Mat object(rows, cols, type);
62-
id = FOCV_Storage::save(object);
41+
42+
cv::Mat mat(rows, cols, type);
43+
memset(mat.data, 0, rows * cols * mat.elemSize1());
44+
45+
if (argCount == 5 && arguments[4].isObject()) {
46+
std::vector<float> vec;
47+
auto rawArray = arguments[4].asObject(runtime);
48+
auto array = rawArray.asArray(runtime);
49+
50+
auto rawLength = rawArray.getProperty(runtime, "length");
51+
auto length = rawLength.asNumber();
52+
53+
for(auto i = 0; i < length; i++) {
54+
vec.push_back(array.getValueAtIndex(runtime, i).asNumber());
55+
}
56+
memcpy(mat.data, vec.data(), vec.size() * mat.elemSize1());
57+
mat = mat.reshape(1, rows);
58+
mat.convertTo(mat, type);
6359
}
60+
61+
id = FOCV_Storage::save(mat);
6462
} break;
6563
case hashString("mat_vector", 10): {
6664
std::vector<cv::Mat> object;

cpp/FOCV_Object.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ using namespace facebook;
3030

3131
class FOCV_Object {
3232
public:
33-
static jsi::Object create(jsi::Runtime& runtime, const jsi::Value* arguments);
33+
static jsi::Object create(jsi::Runtime& runtime, const jsi::Value* arguments, size_t argCount);
3434
static jsi::Object convertToJSI(jsi::Runtime& runtime, const jsi::Value* arguments);
3535
static jsi::Object copyObjectFromVector(jsi::Runtime& runtime, const jsi::Value* arguments);
3636
};

cpp/react-native-fast-opencv.cpp

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -47,36 +47,51 @@ jsi::Value OpenCVPlugin::get(jsi::Runtime& runtime, const jsi::PropNameID& propN
4747
double rows = arguments[0].asNumber();
4848
double cols = arguments[1].asNumber();
4949
int channels = arguments[2].asNumber();
50-
jsi::Object input = arguments[3].asObject(runtime);
50+
std::string dataType = arguments[3].asString(runtime).utf8(runtime);
51+
jsi::Object input = arguments[4].asObject(runtime);
5152

5253
int type = -1;
5354
if (channels == 1) {
54-
type = CV_8U;
55+
type = dataType == "float32" ? CV_32F : CV_8U;
5556
}
5657
if (channels == 3) {
57-
type = CV_8UC3;
58+
type = dataType == "float32" ? CV_32FC3 : CV_8UC3;
5859
}
5960
if (channels == 4) {
60-
type = CV_8UC4;
61+
type = dataType == "float32" ? CV_32FC4 : CV_8UC4;
6162
}
6263

6364
if (channels == -1) {
6465
throw std::runtime_error("Fast OpenCV Error: Invalid channel count passed to frameBufferToMat!");
6566
}
67+
68+
TypedArrayKind typedArrayType = dataType == "float32" ? TypedArrayKind::Float32Array : TypedArrayKind::Uint8Array;
69+
70+
if (dataType == "float32") {
71+
TypedArrayBase inputBuffer = getTypedArray(runtime, std::move(input));
72+
auto vec = inputBuffer.toVector(runtime);
73+
74+
cv::Mat mat(rows, cols, type);
75+
memcpy(mat.data, vec.data(), (int)rows * (int)cols * channels * 4);
76+
auto id = FOCV_Storage::save(mat);
77+
78+
return FOCV_JsiObject::wrap(runtime, "mat", id);
79+
}
80+
else {
81+
TypedArrayBase inputBuffer = getTypedArray(runtime, std::move(input));
82+
auto vec = inputBuffer.toVector(runtime);
6683

67-
TypedArrayBase inputBuffer = getTypedArray(runtime, std::move(input));
68-
auto vec = inputBuffer.toVector(runtime);
69-
70-
cv::Mat mat(rows, cols, type);
71-
memcpy(mat.data, vec.data(), (int)rows * (int)cols * channels);
72-
auto id = FOCV_Storage::save(mat);
73-
74-
return FOCV_JsiObject::wrap(runtime, "mat", id);
84+
cv::Mat mat(rows, cols, type);
85+
memcpy(mat.data, vec.data(), (int)rows * (int)cols * channels);
86+
auto id = FOCV_Storage::save(mat);
87+
88+
return FOCV_JsiObject::wrap(runtime, "mat", id);
89+
}
7590
});
7691
}
7792
else if (propName == "base64ToMat") {
7893
return jsi::Function::createFromHostFunction(
79-
runtime, jsi::PropNameID::forAscii(runtime, "frameBufferToMat"), 1,
94+
runtime, jsi::PropNameID::forAscii(runtime, "base64ToMat"), 1,
8095
[=](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments,
8196
size_t count) -> jsi::Object {
8297

@@ -123,8 +138,8 @@ jsi::Value OpenCVPlugin::get(jsi::Runtime& runtime, const jsi::PropNameID& propN
123138
runtime, jsi::PropNameID::forAscii(runtime, "createObject"), 1,
124139
[=](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments,
125140
size_t count) -> jsi::Object {
126-
127-
return FOCV_Object::create(runtime, arguments);
141+
142+
return FOCV_Object::create(runtime, arguments, count);
128143
});
129144
}
130145
else if (propName == "toJSValue") {

example/src/examples/CameraAffineTransform.tsx

Lines changed: 61 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,12 @@ import {
99
} from '@shopify/react-native-skia';
1010
import { useEffect } from 'react';
1111
import { SafeAreaView, StyleSheet, Text, View } from 'react-native';
12-
import { DataTypes, ObjectType, OpenCV } from 'react-native-fast-opencv';
12+
import {
13+
ColorConversionCodes,
14+
DataTypes,
15+
ObjectType,
16+
OpenCV,
17+
} from 'react-native-fast-opencv';
1318
import { useSharedValue } from 'react-native-reanimated';
1419
import {
1520
Camera,
@@ -22,10 +27,10 @@ import { useResizePlugin, type Options } from 'vision-camera-resize-plugin';
2227

2328
type PixelFormat = Options<'uint8'>['pixelFormat'];
2429

25-
const WIDTH = 300;
26-
const HEIGHT = 300;
27-
const TARGET_TYPE = 'uint8' as const;
28-
const TARGET_FORMAT: PixelFormat = 'rgba';
30+
const WIDTH = 50;
31+
const HEIGHT = 50;
32+
const TARGET_TYPE = 'float32' as const;
33+
const TARGET_FORMAT: PixelFormat = 'rgb';
2934

3035
let lastWarn: PixelFormat | undefined;
3136
lastWarn = undefined;
@@ -114,6 +119,7 @@ export function CameraAffineTransform() {
114119
(frame) => {
115120
'worklet';
116121

122+
// Step 1. Resize the frame to the target size
117123
const resized = resize(frame, {
118124
scale: {
119125
width: WIDTH,
@@ -130,41 +136,77 @@ export function CameraAffineTransform() {
130136
rotation: '90deg',
131137
});
132138

133-
const frameMat = OpenCV.frameBufferToMat(HEIGHT, WIDTH, 4, resized);
134-
const outputMat = OpenCV.createObject(
135-
ObjectType.Mat,
139+
// Step 2. Create an OpenCV Mat from the resized frame
140+
const frameMat = OpenCV.frameBufferToMat(
136141
HEIGHT,
137142
WIDTH,
138-
DataTypes.CV_8UC4
143+
3,
144+
'float32',
145+
resized
139146
);
140147

148+
// Step 3. Apply affine transform to the frame
149+
const transformedMat = OpenCV.createObject(
150+
ObjectType.Mat,
151+
HEIGHT,
152+
WIDTH,
153+
DataTypes.CV_32FC3,
154+
undefined
155+
);
141156
const scaleX = frame.height / frame.width;
142157
const scaledFrameWidth = WIDTH * scaleX;
143158
const translateX = (scaledFrameWidth - WIDTH) / 2;
144-
145159
// 2x3 affine matrix has the following form (for scale and translation only):
146160
// | sx 0 tx |
147161
// | 0 sy ty |
148162
const matrix = [
149163
[scaleX, 0.0, -translateX],
150164
[0.0, 1.0, 0.0],
151165
];
152-
153166
const transformMat = OpenCV.createObject(
154167
ObjectType.Mat,
155168
2,
156169
3,
157170
DataTypes.CV_32F,
158171
matrix.flat()
159172
);
173+
OpenCV.invoke('warpAffine', frameMat, transformedMat, transformMat);
160174

161-
OpenCV.invoke('warpAffine', frameMat, outputMat, transformMat);
162-
163-
const output = OpenCV.matToBuffer(outputMat, 'uint8');
164-
const data = Skia.Data.fromBytes(output.buffer);
175+
// Step 4. Convert the transformed frame to RGBA
176+
const uint8TransformedMat = OpenCV.createObject(
177+
ObjectType.Mat,
178+
HEIGHT,
179+
WIDTH,
180+
DataTypes.CV_8UC3,
181+
undefined
182+
);
183+
OpenCV.invoke(
184+
'convertTo',
185+
transformedMat,
186+
uint8TransformedMat,
187+
DataTypes.CV_8UC3,
188+
255.0,
189+
0
190+
);
191+
const rgbaOutputMat = OpenCV.createObject(
192+
ObjectType.Mat,
193+
HEIGHT,
194+
WIDTH,
195+
DataTypes.CV_8UC4,
196+
undefined
197+
);
198+
OpenCV.invoke(
199+
'cvtColor',
200+
uint8TransformedMat,
201+
rgbaOutputMat,
202+
ColorConversionCodes.COLOR_RGB2RGBA
203+
);
165204

166-
updatePreviewImageFromData(data, TARGET_FORMAT).then(() => {
167-
data.dispose();
205+
// Step 5. Convert the transformed frame to Skia Image
206+
const output = OpenCV.matToBuffer(rgbaOutputMat, 'uint8');
207+
const outputData = Skia.Data.fromBytes(output.buffer);
208+
updatePreviewImageFromData(outputData, 'rgba').then(() => {
209+
outputData.dispose();
168210
});
169211

170212
OpenCV.clearBuffers(); // REMEMBER TO CLEAN
@@ -224,5 +266,7 @@ const styles = StyleSheet.create({
224266
height: HEIGHT,
225267
borderColor: 'red',
226268
borderWidth: 2,
269+
transformOrigin: 'bottom',
270+
transform: [{ scale: 4 }],
227271
},
228272
});

example/src/examples/CameraPassthrough.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,13 @@ export function CameraPassthrough() {
130130
rotation: '90deg',
131131
});
132132

133-
const frameMat = OpenCV.frameBufferToMat(HEIGHT, WIDTH, 4, resized);
133+
const frameMat = OpenCV.frameBufferToMat(
134+
HEIGHT,
135+
WIDTH,
136+
4,
137+
'uint8',
138+
resized
139+
);
134140
const output = OpenCV.matToBuffer(frameMat, 'uint8');
135141
const data = Skia.Data.fromBytes(output.buffer);
136142

example/src/examples/CameraRealtimeDetection.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export function CameraRealtimeDetection() {
4747
dataType: 'uint8',
4848
});
4949

50-
const src = OpenCV.frameBufferToMat(height, width, 3, resized);
50+
const src = OpenCV.frameBufferToMat(height, width, 3, 'uint8', resized);
5151
const dst = OpenCV.createObject(ObjectType.Mat, 0, 0, DataTypes.CV_8U);
5252

5353
const lowerBound = OpenCV.createObject(ObjectType.Scalar, 30, 60, 60);

src/utils/UtilsFunctions.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,12 @@ export type UtilsFunctions = {
1212
* @param channels - the number of channels in the Mat
1313
* @param input - the byte array to convert
1414
*/
15-
frameBufferToMat(
15+
frameBufferToMat<T extends 'uint8' | 'float32'>(
1616
rows: number,
1717
cols: number,
1818
channels: number,
19-
input: Uint8Array
19+
type: T,
20+
input: T extends 'uint8' ? Uint8Array : Float32Array
2021
): Mat;
2122
/**
2223
* Converts a base64 string to a Mat.

0 commit comments

Comments
 (0)