Skip to content

Commit 7808ff0

Browse files
committed
C++ and library part of image embeddings.
1 parent 0787eca commit 7808ff0

14 files changed

Lines changed: 202 additions & 2 deletions

File tree

packages/react-native-executorch/common/rnexecutorch/RnExecutorchInstaller.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <rnexecutorch/TokenizerModule.h>
44
#include <rnexecutorch/host_objects/JsiConversions.h>
55
#include <rnexecutorch/models/classification/Classification.h>
6+
#include <rnexecutorch/models/image_embeddings/ImageEmbeddings.h>
67
#include <rnexecutorch/models/image_segmentation/ImageSegmentation.h>
78
#include <rnexecutorch/models/object_detection/ObjectDetection.h>
89
#include <rnexecutorch/models/style_transfer/StyleTransfer.h>
@@ -48,6 +49,11 @@ void RnExecutorchInstaller::injectJSIBindings(
4849
*jsiRuntime, "loadTokenizerModule",
4950
RnExecutorchInstaller::loadModel<TokenizerModule>(
5051
jsiRuntime, jsCallInvoker, "loadTokenizerModule"));
52+
53+
jsiRuntime->global().setProperty(
54+
*jsiRuntime, "loadImageEmbeddings",
55+
RnExecutorchInstaller::loadModel<ImageEmbeddings>(
56+
jsiRuntime, jsCallInvoker, "loadImageEmbeddings"));
5157
}
5258

5359
} // namespace rnexecutorch

packages/react-native-executorch/common/rnexecutorch/RnExecutorchInstaller.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ REGISTER_CONSTRUCTOR(BaseModel, std::string,
3030
std::shared_ptr<react::CallInvoker>);
3131
REGISTER_CONSTRUCTOR(TokenizerModule, std::string,
3232
std::shared_ptr<react::CallInvoker>);
33+
REGISTER_CONSTRUCTOR(ImageEmbeddings, std::string,
34+
std::shared_ptr<react::CallInvoker>);
3335

3436
using namespace facebook;
3537

packages/react-native-executorch/common/rnexecutorch/TokenizerModule.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
#include "TokenizerModule.h"
22
#include <executorch/extension/module/module.h>
33
#include <filesystem>
4-
#include <rnexecutorch/Log.h>
54
#include <rnexecutorch/data_processing/FileUtils.h>
65

76
namespace rnexecutorch {

packages/react-native-executorch/common/rnexecutorch/data_processing/Numerical.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "Numerical.h"
22

33
#include <algorithm>
4+
#include <cmath>
45
#include <numeric>
56

67
namespace rnexecutorch::numerical {
@@ -16,4 +17,19 @@ void softmax(std::vector<float> &v) {
1617
x /= sum;
1718
}
1819
}
20+
void normalizeVector(std::vector<float> &v) {
21+
float norm = 0.0;
22+
for (float value : v) {
23+
norm += value * value;
24+
}
25+
norm = sqrt(norm);
26+
27+
if (norm == 0) {
28+
return;
29+
}
30+
31+
for (float &value : v) {
32+
value /= norm;
33+
}
34+
}
1935
} // namespace rnexecutorch::numerical

packages/react-native-executorch/common/rnexecutorch/data_processing/Numerical.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@
44

55
namespace rnexecutorch::numerical {
66
void softmax(std::vector<float> &v);
7+
void normalizeVector(std::vector<float> &v);
78
} // namespace rnexecutorch::numerical

packages/react-native-executorch/common/rnexecutorch/host_objects/JsiConversions.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,21 @@ inline jsi::Value getJsiValue(const std::vector<JSTensorViewOut> &vec,
216216
return jsi::Value(runtime, array);
217217
}
218218

219+
inline jsi::Value getJsiValue(const JSTensorViewOut &tensor,
220+
jsi::Runtime &runtime) {
221+
jsi::Object tensorObj(runtime);
222+
223+
tensorObj.setProperty(runtime, "sizes", getJsiValue(tensor.sizes, runtime));
224+
225+
tensorObj.setProperty(runtime, "scalarType",
226+
jsi::Value(static_cast<int>(tensor.scalarType)));
227+
228+
jsi::ArrayBuffer arrayBuffer(runtime, tensor.dataPtr);
229+
tensorObj.setProperty(runtime, "dataPtr", arrayBuffer);
230+
231+
return tensorObj;
232+
}
233+
219234
inline jsi::Value getJsiValue(const std::string &str, jsi::Runtime &runtime) {
220235
return jsi::String::createFromAscii(runtime, str);
221236
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#include "ImageEmbeddings.h"
2+
3+
#include <executorch/extension/tensor/tensor.h>
4+
#include <rnexecutorch/Log.h>
5+
#include <rnexecutorch/data_processing/ImageProcessing.h>
6+
#include <rnexecutorch/data_processing/Numerical.h>
7+
namespace rnexecutorch {
8+
9+
ImageEmbeddings::ImageEmbeddings(
10+
const std::string &modelSource,
11+
std::shared_ptr<react::CallInvoker> callInvoker)
12+
: BaseModel(modelSource, callInvoker) {
13+
auto inputTensors = getAllInputShapes();
14+
if (inputTensors.size() == 0) {
15+
throw std::runtime_error("Model seems to not take any input tensors.");
16+
}
17+
std::vector<int32_t> modelInputShape = inputTensors[0];
18+
if (modelInputShape.size() < 2) {
19+
char errorMessage[100];
20+
std::snprintf(errorMessage, sizeof(errorMessage),
21+
"Unexpected model input size, expected at least 2 dimentions "
22+
"but got: %zu.",
23+
modelInputShape.size());
24+
throw std::runtime_error(errorMessage);
25+
}
26+
modelImageSize = cv::Size(modelInputShape[modelInputShape.size() - 1],
27+
modelInputShape[modelInputShape.size() - 2]);
28+
}
29+
30+
JSTensorViewOut ImageEmbeddings::generate(std::string imageSource) {
31+
auto [inputTensor, originalSize] =
32+
imageprocessing::readImageToTensor(imageSource, getAllInputShapes()[0]);
33+
34+
auto result = BaseModel::forward(inputTensor);
35+
if (!result.ok()) {
36+
throw std::runtime_error("Forward pass failed: Error " +
37+
std::to_string(static_cast<int>(result.error())));
38+
}
39+
40+
auto &outputs = result.get();
41+
42+
if (outputs.size() > 1) {
43+
throw std::runtime_error("It returned multiple outputs!");
44+
}
45+
46+
auto &outputTensor = outputs.at(0).toTensor();
47+
auto sizesRaw = outputTensor.sizes();
48+
auto sizes = std::vector<int32_t>(sizesRaw.begin(), sizesRaw.end());
49+
size_t bufferSize = outputTensor.numel() * outputTensor.element_size();
50+
auto buffer = std::make_shared<OwningArrayBuffer>(bufferSize);
51+
52+
std::span<const float> outputTensorSpan(
53+
static_cast<const float *>(outputTensor.const_data_ptr()),
54+
outputTensor.numel());
55+
std::vector<float> outputVector(outputTensorSpan.begin(),
56+
outputTensorSpan.end());
57+
58+
numerical::normalizeVector(outputVector);
59+
60+
std::memcpy(buffer->data(), outputVector.data(), bufferSize);
61+
62+
auto jsTensor = JSTensorViewOut(sizes, outputTensor.scalar_type(), buffer);
63+
64+
return jsTensor;
65+
}
66+
} // namespace rnexecutorch
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#pragma once
2+
3+
#include <executorch/extension/tensor/tensor_ptr.h>
4+
#include <executorch/runtime/core/evalue.h>
5+
#include <opencv2/opencv.hpp>
6+
7+
#include <rnexecutorch/models/BaseModel.h>
8+
9+
namespace rnexecutorch {
10+
using executorch::extension::TensorPtr;
11+
using executorch::runtime::EValue;
12+
13+
class ImageEmbeddings : public BaseModel {
14+
public:
15+
ImageEmbeddings(const std::string &modelSource,
16+
std::shared_ptr<react::CallInvoker> callInvoker);
17+
JSTensorViewOut generate(std::string imageSource);
18+
19+
private:
20+
cv::Size modelImageSize{0, 0};
21+
};
22+
23+
} // namespace rnexecutorch

packages/react-native-executorch/src/constants/modelUrls.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,10 @@ export const DETECTOR_CRAFT_320 =
162162
export const DEEPLAB_V3_RESNET50 =
163163
'https://huggingface.co/software-mansion/react-native-executorch-deeplab-v3/resolve/v0.4.0/xnnpack/deeplabV3_xnnpack_fp32.pte';
164164

165+
// Image Embeddings
166+
export const CLIP_VIT_BASE_PATCH_32_IMAGE_ENCODER_MODEL =
167+
'https://huggingface.co/software-mansion/react-native-executorch-clip-vit-base-patch32-image-encoder/resolve/v0.5.0/clip-vit-base-patch32-image-encoder-float32.pte';
168+
165169
// Text Embeddings
166170
export const ALL_MINILM_L6_V2 =
167171
'https://huggingface.co/software-mansion/react-native-executorch-all-MiniLM-L6-v2/resolve/v0.4.0/all-MiniLM-L6-v2_xnnpack.pte';
@@ -183,6 +187,17 @@ export const MULTI_QA_MPNET_BASE_DOT_V1 =
183187
export const MULTI_QA_MPNET_BASE_DOT_V1_TOKENIZER =
184188
'https://huggingface.co/software-mansion/react-native-executorch-multi-qa-mpnet-base-dot-v1/resolve/v0.4.0/tokenizer.json';
185189

190+
export const CLIP_VIT_BASE_PATCH_32_TEXT_ENCODER_MODEL =
191+
'https://huggingface.co/software-mansion/react-native-executorch-clip-vit-base-patch32-text-encoder/resolve/main/clip-vit-base-patch32-text-encoder-float32.pte';
192+
export const CLIP_VIT_BASE_PATCH_32_TEXT_ENCODER_TOKENIZER =
193+
'https://huggingface.co/software-mansion/react-native-executorch-clip-vit-base-patch32-text-encoder/resolve/main/tokenizer.json';
194+
195+
export const CLIP_VIT_BASE_PATCH_32_TEXT_ENCODER = {
196+
modelSource: CLIP_VIT_BASE_PATCH_32_TEXT_ENCODER_MODEL,
197+
tokenizerSource: CLIP_VIT_BASE_PATCH_32_TEXT_ENCODER_TOKENIZER,
198+
meanPooling: false,
199+
};
200+
186201
// Backward compatibility
187202
export const LLAMA3_2_3B_URL = LLAMA3_2_3B;
188203
export const LLAMA3_2_3B_QLORA_URL = LLAMA3_2_3B_QLORA;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { ImageEmbeddingsModule } from '../../modules/computer_vision/ImageEmbeddingsModule';
2+
import { ResourceSource } from '../../types/common';
3+
import { useNonStaticModule } from '../useNonStaticModule';
4+
5+
export const useImageEmbeddings = ({
6+
modelSource,
7+
preventLoad = false,
8+
}: {
9+
modelSource: ResourceSource;
10+
preventLoad?: boolean;
11+
}) =>
12+
useNonStaticModule({
13+
module: ImageEmbeddingsModule,
14+
loadArgs: [modelSource],
15+
preventLoad,
16+
});

0 commit comments

Comments
 (0)