-
Notifications
You must be signed in to change notification settings - Fork 75
Expand file tree
/
Copy pathImageProcessing.cpp
More file actions
152 lines (125 loc) · 5 KB
/
ImageProcessing.cpp
File metadata and controls
152 lines (125 loc) · 5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#include "ImageProcessing.h"
#include <chrono>
#include <filesystem>
#include <ada/ada.h>
#include <rnexecutorch/RnExecutorchInstaller.h>
#include <rnexecutorch/data_processing/FileUtils.h>
#include <rnexecutorch/data_processing/base64.h>
namespace rnexecutorch {
// This is defined in RnExecutorchInstaller.cpp. This function fetches data
// from a url address. It is implemented in Kotlin/ObjectiveC++ and then bound
// to this variable. It's done to not handle SSL intricacies manually, as it is
// done automagically in ObjC++/Kotlin.
extern FetchUrlFunc_t fetchUrlFunc;
namespace imageprocessing {
std::vector<float> colorMatToVector(const cv::Mat &mat) {
return colorMatToVector(mat, cv::Scalar(0.0, 0.0, 0.0),
cv::Scalar(1.0, 1.0, 1.0));
}
std::vector<float> colorMatToVector(const cv::Mat &mat, cv::Scalar mean,
cv::Scalar variance) {
int pixelCount = mat.cols * mat.rows;
std::vector<float> v(pixelCount * 3);
for (int i = 0; i < pixelCount; i++) {
int row = i / mat.cols;
int col = i % mat.cols;
cv::Vec3b pixel = mat.at<cv::Vec3b>(row, col);
v[0 * pixelCount + i] =
(pixel[0] - mean[0] * 255.0) / (variance[0] * 255.0);
v[1 * pixelCount + i] =
(pixel[1] - mean[1] * 255.0) / (variance[1] * 255.0);
v[2 * pixelCount + i] =
(pixel[2] - mean[2] * 255.0) / (variance[2] * 255.0);
}
return v;
}
cv::Mat bufferToColorMat(const std::span<const float> &buffer,
cv::Size matSize) {
cv::Mat mat(matSize, CV_8UC3);
int pixelCount = matSize.width * matSize.height;
for (int i = 0; i < pixelCount; i++) {
int row = i / matSize.width;
int col = i % matSize.width;
float r = buffer[0 * pixelCount + i];
float g = buffer[1 * pixelCount + i];
float b = buffer[2 * pixelCount + i];
cv::Vec3b color(static_cast<uchar>(b * 255), static_cast<uchar>(g * 255),
static_cast<uchar>(r * 255));
mat.at<cv::Vec3b>(row, col) = color;
}
return mat;
}
std::string saveToTempFile(const cv::Mat &image) {
std::string filename = "rn_executorch_" + fileutils::getTimeID() + ".png";
std::filesystem::path tempDir = std::filesystem::temp_directory_path();
std::filesystem::path filePath = tempDir / filename;
if (!cv::imwrite(filePath.string(), image)) {
throw std::runtime_error("Failed to save the image: " + filePath.string());
}
return "file://" + filePath.string();
}
cv::Mat readImage(const std::string &imageURI) {
cv::Mat image;
if (imageURI.starts_with("data")) {
// base64
std::stringstream uriStream(imageURI);
std::string stringData;
std::size_t segmentIndex{0};
while (std::getline(uriStream, stringData, ',')) {
++segmentIndex;
}
if (segmentIndex != 1) {
throw std::runtime_error("Read image error: invalid base64 URI");
}
auto data = base64_decode(stringData);
cv::Mat encodedData(1, data.size(), CV_8UC1, (void *)data.data());
image = cv::imdecode(encodedData, cv::IMREAD_COLOR);
} else if (imageURI.starts_with("file")) {
// local file
auto url = ada::parse(imageURI);
image = cv::imread(std::string{url->get_pathname()}, cv::IMREAD_COLOR);
} else if (imageURI.starts_with("http")) {
// remote file
std::vector<std::byte> imageData = fetchUrlFunc(imageURI);
image = cv::imdecode(
cv::Mat(1, imageData.size(), CV_8UC1, (void *)imageData.data()),
cv::IMREAD_COLOR);
} else {
throw std::runtime_error("Read image error: unknown protocol");
}
if (image.empty()) {
throw std::runtime_error("Read image error: invalid argument");
}
return image;
}
TensorPtr getTensorFromMatrix(const std::vector<int32_t> &tensorDims,
const cv::Mat &matrix) {
std::vector<float> inputVector = colorMatToVector(matrix);
return executorch::extension::make_tensor_ptr(tensorDims, inputVector);
}
cv::Mat getMatrixFromTensor(cv::Size size, const Tensor &tensor) {
auto resultData = static_cast<const float *>(tensor.const_data_ptr());
return bufferToColorMat(std::span<const float>(resultData, tensor.numel()),
size);
}
std::pair<TensorPtr, cv::Size>
readImageToTensor(const std::string &path,
const std::vector<int32_t> &tensorDims) {
cv::Mat input = imageprocessing::readImage(path);
cv::Size imageSize = input.size();
if (tensorDims.size() < 2) {
char errorMessage[100];
std::snprintf(errorMessage, sizeof(errorMessage),
"Unexpected tensor size, expected at least 2 dimentions "
"but got: %zu.",
tensorDims.size());
throw std::runtime_error(errorMessage);
}
cv::Size tensorSize = cv::Size(tensorDims[tensorDims.size() - 1],
tensorDims[tensorDims.size() - 2]);
cv::resize(input, input, tensorSize);
cv::cvtColor(input, input, cv::COLOR_BGR2RGB);
return {imageprocessing::getTensorFromMatrix(tensorDims, input), imageSize};
}
} // namespace imageprocessing
} // namespace rnexecutorch