Skip to content

Commit 564b937

Browse files
feat: mat to base64 to mat methods
1 parent 730f9eb commit 564b937

File tree

7 files changed

+252
-43
lines changed

7 files changed

+252
-43
lines changed

cpp/ConvertImage.cpp

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
//
2+
// ConvertImage.cpp
3+
// react-native-fast-opencv
4+
//
5+
// Created by Łukasz Kurant on 13/08/2024.
6+
//
7+
8+
#include "ConvertImage.hpp"
9+
#include <opencv2/opencv.hpp>
10+
#include <opencv2/core.hpp>
11+
#include <opencv2/imgcodecs.hpp>
12+
13+
#include <vector>
14+
#include <string>
15+
16+
using namespace std;
17+
using namespace cv;
18+
19+
static const std::string base64_chars =
20+
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
21+
"abcdefghijklmnopqrstuvwxyz"
22+
"0123456789+/";
23+
24+
static inline bool is_base64( unsigned char c )
25+
{
26+
return (isalnum(c) || (c == '+') || (c == '/'));
27+
}
28+
29+
std::string ImageConverter::base64_encode(uchar const* bytes_to_encode, unsigned int in_len)
30+
{
31+
std::string ret;
32+
33+
int i = 0;
34+
int j = 0;
35+
unsigned char char_array_3[3];
36+
unsigned char char_array_4[4];
37+
38+
while (in_len--)
39+
{
40+
char_array_3[i++] = *(bytes_to_encode++);
41+
if (i == 3)
42+
{
43+
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
44+
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
45+
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
46+
char_array_4[3] = char_array_3[2] & 0x3f;
47+
48+
for (i = 0; (i <4); i++)
49+
{
50+
ret += base64_chars[char_array_4[i]];
51+
}
52+
i = 0;
53+
}
54+
}
55+
56+
if (i)
57+
{
58+
for (j = i; j < 3; j++)
59+
{
60+
char_array_3[j] = '\0';
61+
}
62+
63+
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
64+
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
65+
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
66+
char_array_4[3] = char_array_3[2] & 0x3f;
67+
68+
for (j = 0; (j < i + 1); j++)
69+
{
70+
ret += base64_chars[char_array_4[j]];
71+
}
72+
73+
while ((i++ < 3))
74+
{
75+
ret += '=';
76+
}
77+
}
78+
79+
return ret;
80+
}
81+
82+
std::string ImageConverter::base64_decode(std::string const& encoded_string)
83+
{
84+
int in_len = encoded_string.size();
85+
int i = 0;
86+
int j = 0;
87+
int in_ = 0;
88+
unsigned char char_array_4[4], char_array_3[3];
89+
std::string ret;
90+
91+
while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_]))
92+
{
93+
char_array_4[i++] = encoded_string[in_]; in_++;
94+
95+
if (i == 4)
96+
{
97+
for (i = 0; i < 4; i++)
98+
{
99+
char_array_4[i] = base64_chars.find(char_array_4[i]);
100+
}
101+
102+
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
103+
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
104+
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
105+
106+
for (i = 0; (i < 3); i++)
107+
{
108+
ret += char_array_3[i];
109+
}
110+
111+
i = 0;
112+
}
113+
}
114+
115+
if (i)
116+
{
117+
for (j = i; j < 4; j++)
118+
{
119+
char_array_4[j] = 0;
120+
}
121+
122+
for (j = 0; j < 4; j++)
123+
{
124+
char_array_4[j] = base64_chars.find(char_array_4[j]);
125+
}
126+
127+
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
128+
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
129+
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
130+
131+
for (j = 0; (j < i - 1); j++)
132+
{
133+
ret += char_array_3[j];
134+
}
135+
}
136+
137+
return ret;
138+
}
139+
140+
string ImageConverter::mat2str(const Mat& m)
141+
{
142+
int params[3] = {0};
143+
params[0] = IMWRITE_JPEG_QUALITY;
144+
params[1] = 100;
145+
146+
vector<uchar> buf;
147+
bool code = cv::imencode(".jpg", m, buf, std::vector<int>(params, params+2));
148+
uchar* result = reinterpret_cast<uchar*> (&buf[0]);
149+
150+
return base64_encode(result, buf.size());
151+
152+
}
153+
154+
Mat ImageConverter::str2mat(const string& s)
155+
{
156+
// Decode data
157+
string decoded_string = base64_decode(s);
158+
vector<uchar> data(decoded_string.begin(), decoded_string.end());
159+
160+
cv::Mat img = imdecode(data, IMREAD_UNCHANGED);
161+
return img;
162+
}

cpp/ConvertImage.hpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//
2+
// ConvertImage.hpp
3+
// react-native-fast-opencv
4+
//
5+
// Created by Łukasz Kurant on 13/08/2024.
6+
//
7+
8+
#ifndef ConvertImage_hpp
9+
#define ConvertImage_hpp
10+
11+
#include <stdio.h>
12+
#include <opencv2/opencv.hpp>
13+
#include <opencv2/core.hpp>
14+
#include <opencv2/imgcodecs.hpp>
15+
16+
#include <vector>
17+
#include <string>
18+
19+
using namespace std;
20+
using namespace cv;
21+
22+
class ImageConverter {
23+
public:
24+
static cv::Mat str2mat(const string& imageBase64);
25+
static string mat2str(const Mat& img);
26+
27+
private:
28+
static std::string base64_encode(uchar const* bytesToEncode, unsigned int inLen);
29+
static std::string base64_decode(std::string const& encodedString);
30+
};
31+
32+
#endif

cpp/FOCV_Object.cpp

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "FOCV_JsiObject.hpp"
1111
#include "jsi/TypedArray.h"
1212
#include <opencv2/opencv.hpp>
13+
#include "ConvertImage.hpp"
1314

1415
constexpr uint64_t hashString(const char* str, size_t length) {
1516
uint64_t hash = 14695981039346656037ull;
@@ -92,33 +93,18 @@ jsi::Object FOCV_Object::create(jsi::Runtime& runtime, const jsi::Value* argumen
9293
}
9394

9495
jsi::Object FOCV_Object::convertToJSI(jsi::Runtime& runtime, const jsi::Value* arguments) {
95-
9696
jsi::Object value(runtime);
9797
std::string objectType = FOCV_JsiObject::type_from_wrap(runtime, arguments[0]);
9898
std::string id = FOCV_JsiObject::id_from_wrap(runtime, arguments[0]);
9999

100100
switch(hashString(objectType.c_str(), objectType.size())) {
101101
case hashString("mat", 3): {
102102
cv::Mat mat = FOCV_Storage::get<cv::Mat>(id);
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-
117103

104+
value.setProperty(runtime, "base64", jsi::String::createFromUtf8(runtime, ImageConverter::mat2str(mat)));
118105
value.setProperty(runtime, "size", jsi::Value(mat.size));
119106
value.setProperty(runtime, "cols", jsi::Value(mat.cols));
120107
value.setProperty(runtime, "rows", jsi::Value(mat.rows));
121-
value.setProperty(runtime, "data", valueArray);
122108
} break;
123109
case hashString("mat_vector", 9): {
124110
std::vector<cv::Mat> mats = FOCV_Storage::get<std::vector<cv::Mat>>(id);

cpp/react-native-fast-opencv.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <FOCV_Storage.hpp>
88
#include <FOCV_Function.hpp>
99
#include "FOCV_Object.hpp"
10+
#include "ConvertImage.hpp"
1011
#include "FOCV_JsiObject.hpp"
1112
#include "opencv2/opencv.hpp"
1213

@@ -35,7 +36,6 @@ void OpenCVPlugin::installOpenCV(jsi::Runtime& runtime, std::shared_ptr<react::C
3536

3637
OpenCVPlugin::OpenCVPlugin(std::shared_ptr<react::CallInvoker> callInvoker) : _callInvoker(callInvoker) {}
3738

38-
3939
jsi::Value OpenCVPlugin::get(jsi::Runtime& runtime, const jsi::PropNameID& propNameId) {
4040
auto propName = propNameId.utf8(runtime);
4141

@@ -55,6 +55,20 @@ jsi::Value OpenCVPlugin::get(jsi::Runtime& runtime, const jsi::PropNameID& propN
5555
return FOCV_JsiObject::wrap(runtime, "mat", id);
5656
});
5757
}
58+
if (propName == "base64ToMat") {
59+
return jsi::Function::createFromHostFunction(
60+
runtime, jsi::PropNameID::forAscii(runtime, "frameBufferToMat"), 1,
61+
[=](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments,
62+
size_t count) -> jsi::Object {
63+
64+
std::string base64 = arguments[0].asString(runtime).utf8(runtime);
65+
66+
auto mat = ImageConverter::str2mat(base64);
67+
auto id = FOCV_Storage::save(mat);
68+
69+
return FOCV_JsiObject::wrap(runtime, "mat", id);
70+
});
71+
}
5872
else if (propName == "createObject") {
5973
return jsi::Function::createFromHostFunction(
6074
runtime, jsi::PropNameID::forAscii(runtime, "createObject"), 1,
@@ -108,6 +122,7 @@ std::vector<jsi::PropNameID> OpenCVPlugin::getPropertyNames(jsi::Runtime& runtim
108122
std::vector<jsi::PropNameID> result;
109123

110124
result.push_back(jsi::PropNameID::forAscii(runtime, "frameBufferToMat"));
125+
result.push_back(jsi::PropNameID::forAscii(runtime, "base64ToMat"));
111126
result.push_back(jsi::PropNameID::forAscii(runtime, "createObject"));
112127
result.push_back(jsi::PropNameID::forAscii(runtime, "toJSValue"));
113128
result.push_back(jsi::PropNameID::forAscii(runtime, "copyObjectFromVector"));

example/src/ImageExample.tsx

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,40 @@ import { useState } from 'react';
22
import { Button, Image, SafeAreaView, Text } from 'react-native';
33
import {
44
ColorConversionCodes,
5-
convertUint8ArrayToBase64,
65
DataTypes,
76
ObjectType,
87
OpenCV,
9-
readImageAsUint8Array,
108
} from 'react-native-fast-opencv';
119
import { launchImageLibrary, type Asset } from 'react-native-image-picker';
1210

1311
export function ImageExample() {
1412
const [photo, setPhoto] = useState<Asset | null>(null);
15-
const [base64, setBase64] = useState<string>('');
13+
const [b64, setB64] = useState<string>('');
1614

1715
const test = async () => {
18-
const result = await launchImageLibrary({ mediaType: 'photo' });
19-
console.log(result);
16+
const result = await launchImageLibrary({
17+
mediaType: 'photo',
18+
includeBase64: true,
19+
});
2020
setPhoto(result.assets?.at(0) || null);
2121
};
2222

2323
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);
24+
if (photo && photo.base64 && photo.width && photo.height) {
25+
const src = OpenCV.base64ToMat(photo.base64);
26+
const dst = OpenCV.createObject(ObjectType.Mat, 0, 0, DataTypes.CV_8U);
27+
28+
OpenCV.invoke('cvtColor', src, dst, ColorConversionCodes.COLOR_BGR2RGB);
29+
OpenCV.invoke('cvtColor', dst, dst, ColorConversionCodes.COLOR_BGR2HSV);
30+
31+
const lowerBound = OpenCV.createObject(ObjectType.Vec3b, 0, 120, 120);
32+
const upperBound = OpenCV.createObject(ObjectType.Vec3b, 255, 255, 255);
33+
34+
OpenCV.invoke('inRange', dst, lowerBound, upperBound, dst);
35+
36+
const result = OpenCV.toJSValue(dst);
37+
setB64(result.base64);
38+
// console.log(array.length);
3339
}
3440
};
3541

@@ -40,9 +46,16 @@ export function ImageExample() {
4046
URI: {photo?.uri} {photo?.height} {photo?.width}
4147
</Text>
4248
<Button title="Process" onPress={process} />
43-
{base64 && photo?.width && photo?.height && (
49+
{photo?.base64 && photo?.width && photo?.height && (
50+
<Image
51+
source={{ uri: 'data:image/jpg;base64,' + photo.base64 }}
52+
height={300}
53+
width={300}
54+
/>
55+
)}
56+
{b64 && photo?.width && photo?.height && (
4457
<Image
45-
source={{ uri: 'data:image/png;base64,' + base64 }}
58+
source={{ uri: 'data:image/png;base64,' + b64 }}
4659
height={300}
4760
width={300}
4861
/>

lefthook.yml

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
pre-commit:
2-
parallel: true
3-
commands:
4-
lint:
5-
glob: "*.{js,ts,jsx,tsx}"
6-
run: npx eslint {staged_files}
7-
types:
8-
glob: "*.{js,ts, jsx, tsx}"
9-
run: npx tsc
1+
# pre-commit:
2+
# parallel: true
3+
# commands:
4+
# lint:
5+
# glob: "*.{js,ts,jsx,tsx}"
6+
# run: npx eslint {staged_files}
7+
# types:
8+
# glob: "*.{js,ts, jsx, tsx}"
9+
# run: npx tsc
1010
commit-msg:
1111
parallel: true
1212
commands:

src/index.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export const OpenCV = global.__loadOpenCV();
5555
export interface OpenCVModel {
5656
clearBuffers(): void;
5757
frameBufferToMat(rows: number, cols: number, input: Uint8Array): Mat;
58+
base64ToMat(path: string): Mat;
5859

5960
// Creation
6061
createObject(
@@ -81,7 +82,7 @@ export interface OpenCVModel {
8182
size: number;
8283
cols: number;
8384
rows: number;
84-
data: Uint8Array;
85+
base64: string;
8586
};
8687
toJSValue(matVector: MatVector): {
8788
array: { size: number; cols: number; rows: number }[];

0 commit comments

Comments
 (0)