Spaces:
Running
Running
File size: 12,645 Bytes
f77c97c |
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 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 |
#include <sstream>
#include <fstream>
#include <vector>
#include <b64/encode.h>
#include <jsoncpp/json/json.h>
#include <opencv2/opencv.hpp>
// base64 to image
#include <boost/archive/iterators/binary_from_base64.hpp>
#include <boost/archive/iterators/transform_width.hpp>
#include <boost/archive/iterators/base64_from_binary.hpp>
/// Parameters used in the API
struct APIParams {
/// A list of images, base64 encoded
std::vector<std::string> data;
/// The maximum number of keypoints to detect for each image
std::vector<int> max_keypoints;
/// The timestamps of the images
std::vector<std::string> timestamps;
/// Whether to convert the images to grayscale
bool grayscale;
/// The height and width of each image
std::vector<std::vector<int>> image_hw;
/// The type of feature detector to use
int feature_type;
/// The rotations of the images
std::vector<double> rotates;
/// The scales of the images
std::vector<double> scales;
/// The reference points of the images
std::vector<std::vector<float>> reference_points;
/// Whether to binarize the descriptors
bool binarize;
};
/**
* @brief Contains the results of a keypoint detector.
*
* @details Stores the keypoints and descriptors for each image.
*/
class KeyPointResults {
public:
KeyPointResults() {}
/**
* @brief Constructor.
*
* @param kp The keypoints for each image.
*/
KeyPointResults(const std::vector<std::vector<cv::KeyPoint>>& kp,
const std::vector<cv::Mat>& desc)
: keypoints(kp), descriptors(desc) {}
/**
* @brief Append keypoints to the result.
*
* @param kpts The keypoints to append.
*/
inline void append_keypoints(std::vector<cv::KeyPoint>& kpts) {
keypoints.emplace_back(kpts);
}
/**
* @brief Append descriptors to the result.
*
* @param desc The descriptors to append.
*/
inline void append_descriptors(cv::Mat& desc) {
descriptors.emplace_back(desc);
}
/**
* @brief Get the keypoints.
*
* @return The keypoints.
*/
inline std::vector<std::vector<cv::KeyPoint>> get_keypoints() {
return keypoints;
}
/**
* @brief Get the descriptors.
*
* @return The descriptors.
*/
inline std::vector<cv::Mat> get_descriptors() {
return descriptors;
}
private:
std::vector<std::vector<cv::KeyPoint>> keypoints;
std::vector<cv::Mat> descriptors;
std::vector<std::vector<float>> scores;
};
/**
* @brief Decodes a base64 encoded string.
*
* @param base64 The base64 encoded string to decode.
* @return The decoded string.
*/
std::string base64_decode(const std::string& base64) {
using namespace boost::archive::iterators;
using It = transform_width<binary_from_base64<std::string::const_iterator>, 8, 6>;
// Find the position of the last non-whitespace character
auto end = base64.find_last_not_of(" \t\n\r");
if (end != std::string::npos) {
// Move one past the last non-whitespace character
end += 1;
}
// Decode the base64 string and return the result
return std::string(It(base64.begin()), It(base64.begin() + end));
}
/**
* @brief Decodes a base64 string into an OpenCV image
*
* @param base64 The base64 encoded string
* @return The decoded OpenCV image
*/
cv::Mat base64_to_image(const std::string& base64) {
// Decode the base64 string
std::string decodedStr = base64_decode(base64);
// Decode the image
std::vector<uchar> data(decodedStr.begin(), decodedStr.end());
cv::Mat img = cv::imdecode(data, cv::IMREAD_GRAYSCALE);
// Check for errors
if (img.empty()) {
throw std::runtime_error("Failed to decode image");
}
return img;
}
/**
* @brief Encodes an OpenCV image into a base64 string
*
* This function takes an OpenCV image and encodes it into a base64 string.
* The image is first encoded as a PNG image, and then the resulting
* bytes are encoded as a base64 string.
*
* @param img The OpenCV image
* @return The base64 encoded string
*
* @throws std::runtime_error if the image is empty or encoding fails
*/
std::string image_to_base64(cv::Mat &img) {
if (img.empty()) {
throw std::runtime_error("Failed to read image");
}
// Encode the image as a PNG
std::vector<uchar> buf;
if (!cv::imencode(".png", img, buf)) {
throw std::runtime_error("Failed to encode image");
}
// Encode the bytes as a base64 string
using namespace boost::archive::iterators;
using It = base64_from_binary<transform_width<std::vector<uchar>::const_iterator, 6, 8>>;
std::string base64(It(buf.begin()), It(buf.end()));
// Pad the string with '=' characters to a multiple of 4 bytes
base64.append((3 - buf.size() % 3) % 3, '=');
return base64;
}
/**
* @brief Callback function for libcurl to write data to a string
*
* This function is used as a callback for libcurl to write data to a string.
* It takes the contents, size, and nmemb as parameters, and writes the data to
* the string.
*
* @param contents The data to write
* @param size The size of the data
* @param nmemb The number of members in the data
* @param s The string to write the data to
* @return The number of bytes written
*/
size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* s) {
size_t newLength = size * nmemb;
try {
// Resize the string to fit the new data
s->resize(s->size() + newLength);
} catch (std::bad_alloc& e) {
// If there's an error allocating memory, return 0
return 0;
}
// Copy the data to the string
std::copy(static_cast<const char*>(contents),
static_cast<const char*>(contents) + newLength,
s->begin() + s->size() - newLength);
return newLength;
}
// Helper functions
/**
* @brief Helper function to convert a type to a Json::Value
*
* This function takes a value of type T and converts it to a Json::Value.
* It is used to simplify the process of converting a type to a Json::Value.
*
* @param val The value to convert
* @return The converted Json::Value
*/
template <typename T>
Json::Value toJson(const T& val) {
return Json::Value(val);
}
/**
* @brief Converts a vector to a Json::Value
*
* This function takes a vector of type T and converts it to a Json::Value.
* Each element in the vector is appended to the Json::Value array.
*
* @param vec The vector to convert to Json::Value
* @return The Json::Value representing the vector
*/
template <typename T>
Json::Value vectorToJson(const std::vector<T>& vec) {
Json::Value json(Json::arrayValue);
for (const auto& item : vec) {
json.append(item);
}
return json;
}
/**
* @brief Converts a nested vector to a Json::Value
*
* This function takes a nested vector of type T and converts it to a Json::Value.
* Each sub-vector is converted to a Json::Value array and appended to the main Json::Value array.
*
* @param vec The nested vector to convert to Json::Value
* @return The Json::Value representing the nested vector
*/
template <typename T>
Json::Value nestedVectorToJson(const std::vector<std::vector<T>>& vec) {
Json::Value json(Json::arrayValue);
for (const auto& subVec : vec) {
json.append(vectorToJson(subVec));
}
return json;
}
/**
* @brief Converts the APIParams struct to a Json::Value
*
* This function takes an APIParams struct and converts it to a Json::Value.
* The Json::Value is a JSON object with the following fields:
* - data: a JSON array of base64 encoded images
* - max_keypoints: a JSON array of integers, max number of keypoints for each image
* - timestamps: a JSON array of timestamps, one for each image
* - grayscale: a JSON boolean, whether to convert images to grayscale
* - image_hw: a nested JSON array, each sub-array contains the height and width of an image
* - feature_type: a JSON integer, the type of feature detector to use
* - rotates: a JSON array of doubles, the rotation of each image
* - scales: a JSON array of doubles, the scale of each image
* - reference_points: a nested JSON array, each sub-array contains the reference points of an image
* - binarize: a JSON boolean, whether to binarize the descriptors
*
* @param params The APIParams struct to convert
* @return The Json::Value representing the APIParams struct
*/
Json::Value paramsToJson(const APIParams& params) {
Json::Value json;
json["data"] = vectorToJson(params.data);
json["max_keypoints"] = vectorToJson(params.max_keypoints);
json["timestamps"] = vectorToJson(params.timestamps);
json["grayscale"] = toJson(params.grayscale);
json["image_hw"] = nestedVectorToJson(params.image_hw);
json["feature_type"] = toJson(params.feature_type);
json["rotates"] = vectorToJson(params.rotates);
json["scales"] = vectorToJson(params.scales);
json["reference_points"] = nestedVectorToJson(params.reference_points);
json["binarize"] = toJson(params.binarize);
return json;
}
template<typename T>
cv::Mat jsonToMat(Json::Value json) {
int rows = json.size();
int cols = json[0].size();
// Create a single array to hold all the data.
std::vector<T> data;
data.reserve(rows * cols);
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
data.push_back(static_cast<T>(json[i][j].asInt()));
}
}
// Create a cv::Mat object that points to the data.
cv::Mat mat(rows, cols, CV_8UC1, data.data()); // Change the type if necessary.
// cv::Mat mat(cols, rows,CV_8UC1, data.data()); // Change the type if necessary.
return mat;
}
/**
* @brief Decodes the response of the server and prints the keypoints
*
* This function takes the response of the server, a JSON string, and decodes
* it. It then prints the keypoints and draws them on the original image.
*
* @param response The response of the server
* @return The keypoints and descriptors
*/
KeyPointResults decode_response(const std::string& response, bool viz=true) {
Json::CharReaderBuilder builder;
Json::CharReader* reader = builder.newCharReader();
Json::Value jsonData;
std::string errors;
// Parse the JSON response
bool parsingSuccessful = reader->parse(response.c_str(),
response.c_str() + response.size(), &jsonData, &errors);
delete reader;
if (!parsingSuccessful) {
// Handle error
std::cout << "Failed to parse the JSON, errors:" << std::endl;
std::cout << errors << std::endl;
return KeyPointResults();
}
KeyPointResults kpts_results;
// Iterate over the images
for (const auto& jsonItem : jsonData) {
auto jkeypoints = jsonItem["keypoints"];
auto jkeypoints_orig = jsonItem["keypoints_orig"];
auto jdescriptors = jsonItem["descriptors"];
auto jscores = jsonItem["scores"];
auto jimageSize = jsonItem["image_size"];
auto joriginalSize = jsonItem["original_size"];
auto jsize = jsonItem["size"];
std::vector<cv::KeyPoint> vkeypoints;
std::vector<float> vscores;
// Iterate over the keypoints
int counter = 0;
for (const auto& keypoint : jkeypoints_orig) {
if (counter < 10) {
// Print the first 10 keypoints
std::cout << keypoint[0].asFloat() << ", "
<< keypoint[1].asFloat() << std::endl;
}
counter++;
// Convert the Json::Value to a cv::KeyPoint
vkeypoints.emplace_back(cv::KeyPoint(keypoint[0].asFloat(),
keypoint[1].asFloat(), 0.0));
}
if (viz && jsonItem.isMember("image_orig")) {
auto jimg_orig = jsonItem["image_orig"];
cv::Mat img = jsonToMat<uchar>(jimg_orig);
cv::imwrite("viz_image_orig.jpg", img);
// Draw keypoints on the image
cv::Mat imgWithKeypoints;
cv::drawKeypoints(img, vkeypoints,
imgWithKeypoints, cv::Scalar(0, 0, 255));
// Write the image with keypoints
std::string filename = "viz_image_orig_keypoints.jpg";
cv::imwrite(filename, imgWithKeypoints);
}
// Iterate over the descriptors
cv::Mat descriptors = jsonToMat<uchar>(jdescriptors);
kpts_results.append_keypoints(vkeypoints);
kpts_results.append_descriptors(descriptors);
}
return kpts_results;
}
|