|
|
|
#include <sstream> |
|
#include <fstream> |
|
#include <vector> |
|
#include <b64/encode.h> |
|
#include <jsoncpp/json/json.h> |
|
#include <opencv2/opencv.hpp> |
|
|
|
|
|
#include <boost/archive/iterators/binary_from_base64.hpp> |
|
#include <boost/archive/iterators/transform_width.hpp> |
|
#include <boost/archive/iterators/base64_from_binary.hpp> |
|
|
|
|
|
struct APIParams { |
|
|
|
std::vector<std::string> data; |
|
|
|
|
|
std::vector<int> max_keypoints; |
|
|
|
|
|
std::vector<std::string> timestamps; |
|
|
|
|
|
bool grayscale; |
|
|
|
|
|
std::vector<std::vector<int>> image_hw; |
|
|
|
|
|
int feature_type; |
|
|
|
|
|
std::vector<double> rotates; |
|
|
|
|
|
std::vector<double> scales; |
|
|
|
|
|
std::vector<std::vector<float>> reference_points; |
|
|
|
|
|
bool binarize; |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
class KeyPointResults { |
|
public: |
|
KeyPointResults() {} |
|
|
|
|
|
|
|
|
|
|
|
|
|
KeyPointResults(const std::vector<std::vector<cv::KeyPoint>>& kp, |
|
const std::vector<cv::Mat>& desc) |
|
: keypoints(kp), descriptors(desc) {} |
|
|
|
|
|
|
|
|
|
|
|
|
|
inline void append_keypoints(std::vector<cv::KeyPoint>& kpts) { |
|
keypoints.emplace_back(kpts); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
inline void append_descriptors(cv::Mat& desc) { |
|
descriptors.emplace_back(desc); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
inline std::vector<std::vector<cv::KeyPoint>> get_keypoints() { |
|
return keypoints; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
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; |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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>; |
|
|
|
|
|
auto end = base64.find_last_not_of(" \t\n\r"); |
|
if (end != std::string::npos) { |
|
|
|
end += 1; |
|
} |
|
|
|
|
|
return std::string(It(base64.begin()), It(base64.begin() + end)); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cv::Mat base64_to_image(const std::string& base64) { |
|
|
|
std::string decodedStr = base64_decode(base64); |
|
|
|
|
|
std::vector<uchar> data(decodedStr.begin(), decodedStr.end()); |
|
cv::Mat img = cv::imdecode(data, cv::IMREAD_GRAYSCALE); |
|
|
|
|
|
if (img.empty()) { |
|
throw std::runtime_error("Failed to decode image"); |
|
} |
|
|
|
return img; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::string image_to_base64(cv::Mat &img) { |
|
if (img.empty()) { |
|
throw std::runtime_error("Failed to read image"); |
|
} |
|
|
|
|
|
std::vector<uchar> buf; |
|
if (!cv::imencode(".png", img, buf)) { |
|
throw std::runtime_error("Failed to encode image"); |
|
} |
|
|
|
|
|
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())); |
|
|
|
|
|
base64.append((3 - buf.size() % 3) % 3, '='); |
|
|
|
return base64; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* s) { |
|
size_t newLength = size * nmemb; |
|
try { |
|
|
|
s->resize(s->size() + newLength); |
|
} catch (std::bad_alloc& e) { |
|
|
|
return 0; |
|
} |
|
|
|
|
|
std::copy(static_cast<const char*>(contents), |
|
static_cast<const char*>(contents) + newLength, |
|
s->begin() + s->size() - newLength); |
|
return newLength; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template <typename T> |
|
Json::Value toJson(const T& val) { |
|
return Json::Value(val); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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(); |
|
|
|
|
|
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())); |
|
} |
|
} |
|
|
|
|
|
cv::Mat mat(rows, cols, CV_8UC1, data.data()); |
|
|
|
|
|
return mat; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
KeyPointResults decode_response(const std::string& response, bool viz=true) { |
|
Json::CharReaderBuilder builder; |
|
Json::CharReader* reader = builder.newCharReader(); |
|
|
|
Json::Value jsonData; |
|
std::string errors; |
|
|
|
|
|
bool parsingSuccessful = reader->parse(response.c_str(), |
|
response.c_str() + response.size(), &jsonData, &errors); |
|
delete reader; |
|
|
|
if (!parsingSuccessful) { |
|
|
|
std::cout << "Failed to parse the JSON, errors:" << std::endl; |
|
std::cout << errors << std::endl; |
|
return KeyPointResults(); |
|
} |
|
|
|
KeyPointResults kpts_results; |
|
|
|
|
|
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; |
|
|
|
|
|
int counter = 0; |
|
for (const auto& keypoint : jkeypoints_orig) { |
|
if (counter < 10) { |
|
|
|
std::cout << keypoint[0].asFloat() << ", " |
|
<< keypoint[1].asFloat() << std::endl; |
|
} |
|
counter++; |
|
|
|
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); |
|
|
|
|
|
cv::Mat imgWithKeypoints; |
|
cv::drawKeypoints(img, vkeypoints, |
|
imgWithKeypoints, cv::Scalar(0, 0, 255)); |
|
|
|
|
|
std::string filename = "viz_image_orig_keypoints.jpg"; |
|
cv::imwrite(filename, imgWithKeypoints); |
|
} |
|
|
|
|
|
cv::Mat descriptors = jsonToMat<uchar>(jdescriptors); |
|
kpts_results.append_keypoints(vkeypoints); |
|
kpts_results.append_descriptors(descriptors); |
|
} |
|
return kpts_results; |
|
} |
|
|