GrabBag/SDK/EpicEye/src/epiceye.cpp
2025-12-10 00:01:32 +08:00

456 lines
16 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <iostream>
#include <map>
#include <string>
#include "epiceye.h"
#include "httplib.h"
#include "nlohmann_json.hpp"
// 连接超时时间300 milliseconds
#define HTTP_CONNECTION_TIMEOUT 300000
// Read 超时时间 30秒
#define HTTP_READ_TIMEOUT 30
// write 超时时间 30秒
#define HTTP_WRITE_TIMEOUT 30
httplib::Result httpGet(std::string ip, std::string uri) {
httplib::Client client(ip);
client.set_connection_timeout(0, HTTP_CONNECTION_TIMEOUT);
client.set_read_timeout(HTTP_READ_TIMEOUT, 0);
client.set_write_timeout(HTTP_WRITE_TIMEOUT, 0);
return client.Get(uri.c_str());
}
httplib::Result httpPost(std::string ip, std::string uri) {
httplib::Client client(ip);
client.set_connection_timeout(0, HTTP_CONNECTION_TIMEOUT);
client.set_read_timeout(HTTP_READ_TIMEOUT, 0);
client.set_write_timeout(HTTP_WRITE_TIMEOUT, 0);
return client.Post(uri.c_str());
}
httplib::Result httpPut(std::string ip, std::string uri, std::string content) {
httplib::Client client(ip);
client.set_connection_timeout(0, HTTP_CONNECTION_TIMEOUT);
client.set_read_timeout(HTTP_READ_TIMEOUT, 0);
client.set_write_timeout(HTTP_WRITE_TIMEOUT, 0);
return client.Put(uri.c_str(), content, "application/json");
}
#ifdef _WIN32
#include <Winsock2.h>
#include <stdio.h>
#endif
#define EPICEYE_SDK_VERSION "3.3.0"
#define EPICRAW2_HEADER_SIZE 40
#define CAMERA_DISCOVERY_PORT 5665
#define JSON_KEY_SN "sn"
#define JSON_KEY_IP "ip"
#define JSON_KEY_ALIAS "alias"
#define JSON_KEY_MODEL "model"
#define JSON_KEY_WIDTH "width"
#define JSON_KEY_HEIGHT "height"
#define JSON_KEY_CAMERA_MATRIX "cameraMatrix"
#define JSON_KEY_DISTORTION "distortion"
#define JSON_KEY_M "m"
#define JSON_KEY_K1 "k1"
#define JSON_KEY_K2 "k2"
#define JSON_KEY_K3 "k3"
#define JSON_KEY_P1 "p1"
#define JSON_KEY_P2 "p2"
namespace TFTech {
static std::map<std::string, std::vector<float>> undistortLutMap;
const std::vector<uint32_t> cameraDiscoveryPorts = {5665, 5666, 5667, 5668};
bool isValid(httplib::Result &res, std::string message) {
if (!res) {
std::cerr << message << ": " << "request failed!" << std::endl;
return false;
}
if (res->status != 200) {
std::cerr << message << ": " << res->status << " " << httplib::detail::status_message(res->status) << std::endl;
return false;
}
return true;
}
std::string EpicEye::getSDKVersion() {
return EPICEYE_SDK_VERSION;
}
bool requestUndistortLut(std::string ip) {
EpicEyeInfo info;
if (!EpicEye::getInfo(ip, info)) {
return false;
}
httplib::Result res = httpGet(ip, "/api/UndistortLut");
if (!res) {
std::cerr << "requestUndistortLut" << ": "
<< "request failed!" << std::endl;
return false;
}
if (res->status != 200) {
if(res->status == 404) {
std::vector<float> emptyArray;
undistortLutMap[ip] = emptyArray;
return true;
} else {
std::cerr << "requestUndistortLut" << ": " << res->status << " " << httplib::detail::status_message(res->status) << std::endl;
return false;
}
}
const float *buffer = reinterpret_cast<const float *>(res->body.c_str());
uint32_t bufferSize = info.height * info.width * 2;
std::vector<float> undistortLut(buffer, buffer + bufferSize);
undistortLutMap[ip] = undistortLut;
return true;
}
//获取相机信息
bool EpicEye::getInfo(std::string ip, EpicEyeInfo &info) {
httplib::Result res = httpGet(ip, "/api/EpicEye/Info");
if (!isValid(res, "getInfo")) {
return false;
}
nlohmann::json infoJson = nlohmann::json::parse(res->body);
if (infoJson.contains(JSON_KEY_SN) &&
infoJson.contains(JSON_KEY_IP) &&
infoJson.contains(JSON_KEY_MODEL) &&
infoJson.contains(JSON_KEY_ALIAS) &&
infoJson.contains(JSON_KEY_WIDTH) &&
infoJson.contains(JSON_KEY_HEIGHT)) {
// 直接赋值,避免聚合初始化问题
info.SN = infoJson[JSON_KEY_SN].get<std::string>();
info.IP = infoJson[JSON_KEY_IP].get<std::string>();
info.model = infoJson[JSON_KEY_MODEL].get<std::string>();
info.alias = infoJson[JSON_KEY_ALIAS].get<std::string>();
info.width = infoJson[JSON_KEY_WIDTH].get<uint32_t>();
info.height = infoJson[JSON_KEY_HEIGHT].get<uint32_t>();
return true;
} else {
std::cerr << "json parse error!" << std::endl;
return false;
}
}
//触发拍照
bool EpicEye::triggerFrame(std::string ip, std::string &frameID, bool pointCloud) {
std::string paramPointCloud = pointCloud ? "true" : "false";
std::string uri = "/api/Frame?pointCloud=" + paramPointCloud;
httplib::Result res = httpPost(ip, uri);
if (!isValid(res, "triggerFrame")) {
return false;
}
frameID = res->body;
return true;
}
bool EpicEye::getImage(std::string ip, std::string frameID, void *imageBuffer, int &pixelByteSize) {
std::string uri = "/api/Frame?frameId=" + frameID;
httplib::Result res = httpGet(ip, uri);
if (!isValid(res, "getImage")) {
return false;
}
const char *bytes = res->body.c_str();
int width = *(const int *)(bytes + 8);
int height = *(const int *)(bytes + 12);
int dataType = *(const int *)(bytes + 16);
int cameraMatrixLength = *(const int *)(bytes + 20);
int distortionLength = *(const int *)(bytes + 24);
int configStrLength = *(const int *)(bytes + 28);
int depthDataLength = *(const int *)(bytes + 32);
int imageDataLength = *(const int *)(bytes + 36);
memcpy(
imageBuffer,
bytes + EPICRAW2_HEADER_SIZE + cameraMatrixLength + distortionLength + configStrLength + depthDataLength,
imageDataLength
);
pixelByteSize = imageDataLength / width / height;
return true;
}
bool EpicEye::getPointCloud(std::string ip, std::string frameID, float *pointCloudBuffer) {
std::string uri = "/api/Frame?frameId=" + frameID;
httplib::Result res = httpGet(ip, uri);
if (!isValid(res, "getPointCloud")) {
return false;
}
if(undistortLutMap.find(ip) == undistortLutMap.end()) {
if(!requestUndistortLut(ip)) {
std::cout << "not using undistortLutMap" << std::endl;
}
}
const std::vector<float> &undistortLut = undistortLutMap[ip];
bool useUndistortLut = undistortLut.size() > 0 ? true: false;
const char *bytes = res->body.c_str();
int width = *(const int *)(bytes + 8);
int height = *(const int *)(bytes + 12);
int dataType = *(const int *)(bytes + 16);
int cameraMatrixLength = *(const int *)(bytes + 20);
int distortionLength = *(const int *)(bytes + 24);
int configStrLength = *(const int *)(bytes + 28);
int depthDataLength = *(const int *)(bytes + 32);
int imageDataLength = *(const int *)(bytes + 36);
double A = *(const double *)(bytes + EPICRAW2_HEADER_SIZE);
double B = *(const double *)(bytes + EPICRAW2_HEADER_SIZE + 16);
double C = *(const double *)(bytes + EPICRAW2_HEADER_SIZE + 32);
double D = *(const double *)(bytes + EPICRAW2_HEADER_SIZE + 40);
const char *data = bytes + EPICRAW2_HEADER_SIZE + cameraMatrixLength + distortionLength + configStrLength;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
float Z = *(const float *)(data + (i * width + j) * 4);
if (Z > 0.1 && Z < 6000) {
uint32_t index = i * width * 2 + j *2;
float u = (float)j;
float v = (float)i;
if(useUndistortLut) {
u = undistortLut[index];
v = undistortLut[index + 1];
}
*(pointCloudBuffer + (i * width + j) * 3) = (float)(Z * (u - B) / A);
*(pointCloudBuffer + (i * width + j) * 3 + 1) = (float)(Z * (v - D) / C);
*(pointCloudBuffer + (i * width + j) * 3 + 2) = Z;
} else {
*(pointCloudBuffer + (i * width + j) * 3) = NAN;
*(pointCloudBuffer + (i * width + j) * 3 + 1) = NAN;
*(pointCloudBuffer + (i * width + j) * 3 + 2) = NAN;
}
}
}
return true;
}
bool EpicEye::getDepth(std::string ip, std::string frameID, float *depth) {
std::string uri = "/api/Depth?frameId=" + frameID;
httplib::Result res = httpGet(ip, uri);
if (!isValid(res, "getDepth")) {
return false;
}
const char *bytes = res->body.c_str();
int width = *(const int *)(bytes + 8);
int height = *(const int *)(bytes + 12);
int dataType = *(const int *)(bytes + 16);
int cameraMatrixLength = *(const int *)(bytes + 20);
int distortionLength = *(const int *)(bytes + 24);
int configStrLength = *(const int *)(bytes + 28);
int depthDataLength = *(const int *)(bytes + 32);
int imageDataLength = *(const int *)(bytes + 36);
memcpy(
depth,
bytes + EPICRAW2_HEADER_SIZE + cameraMatrixLength + distortionLength + configStrLength,
depthDataLength
);
return true;
}
bool EpicEye::getConfig(std::string ip, nlohmann::json &configJson) {
std::string uri = "/api/CameraConfig";
httplib::Result res = httpGet(ip, uri);
if (!isValid(res, "getConfig")) {
return false;
}
try {
configJson = nlohmann::json::parse(res->body);
return true;
} catch(...) {
std::cerr << "json parse error!" << std::endl;
return false;
}
}
bool EpicEye::setConfig(std::string ip, const nlohmann::json &configJson) {
std::string uri = "/api/CameraConfig";
std::string content = configJson.dump();
httplib::Result res = httpPut(ip, uri, content);
if (!isValid(res, "setConfig")) {
return false;
}
return true;
}
bool EpicEye::getCameraMatrix(std::string ip, std::vector<float> &cameraMatrix) {
std::string uri = "/api/CameraParameters/Intrinsic/CameraMatrix";
httplib::Result res = httpGet(ip, uri);
if (!isValid(res, "getIntrinsic")) {
return false;
}
nlohmann::json cameraMatrixJson = nlohmann::json::parse(res->body);
cameraMatrix.clear();
for (const auto &element : cameraMatrixJson) {
cameraMatrix.push_back(element);
}
return true;
}
bool EpicEye::getDistortion(std::string ip, std::vector<float> &distortion) {
std::string uri = "/api/CameraParameters/Intrinsic/Distortion";
httplib::Result res = httpGet(ip, uri);
if (!isValid(res, "getDistortion")) {
return false;
}
nlohmann::json distortionJson = nlohmann::json::parse(res->body);
distortion.clear();
for (const auto &element : distortionJson) {
distortion.push_back(element);
}
return true;
}
bool EpicEye::searchCamera(std::vector<EpicEyeInfo> &cameraList) {
cameraList.clear();
int socketUDPv4 = socket(AF_INET, SOCK_DGRAM, 0);
int socketUDPv6 = socket(AF_INET6, SOCK_DGRAM, 0);
if (socketUDPv4 < 0 || socketUDPv6 < 0) {
return false;
}
struct sockaddr_in addr_serv_v4;
memset(&addr_serv_v4, 0, sizeof(struct sockaddr_in));
addr_serv_v4.sin_family = AF_INET;
addr_serv_v4.sin_port = htons(CAMERA_DISCOVERY_PORT);
addr_serv_v4.sin_addr.s_addr = htonl(INADDR_ANY);
struct sockaddr_in6 addr_serv_v6;
memset(&addr_serv_v6, 0, sizeof(struct sockaddr_in6));
addr_serv_v6.sin6_family = AF_INET6;
addr_serv_v6.sin6_port = htons(CAMERA_DISCOVERY_PORT);
addr_serv_v6.sin6_addr = in6addr_any;
int len_v4, len_v6;
len_v4 = sizeof(addr_serv_v4);
len_v6 = sizeof(addr_serv_v6);
#ifdef _WIN32
int nNetTimeout = 4000;
if (setsockopt(socketUDPv4, SOL_SOCKET, SO_RCVTIMEO, (char*)&nNetTimeout, sizeof(int)) < 0 ||
setsockopt(socketUDPv6, SOL_SOCKET, SO_RCVTIMEO, (char*)&nNetTimeout, sizeof(int)) < 0) {
std::cerr << "searchCamera:: can not set timeout for udp socket!" << std::endl;
return false;
}
#else
struct timeval tv;
tv.tv_sec = 4;
tv.tv_usec = 0;
if (setsockopt(socketUDPv4, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0 ||
setsockopt(socketUDPv6, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
std::cerr << "searchCamera:: can not set timeout for udp socket!" << std::endl;
return false;
}
#endif
bool bindv4Success = false, bindv6Success = false;
for (const auto& port : cameraDiscoveryPorts) {
if (!bindv4Success) {
addr_serv_v4.sin_port = htons(port);
if (bind(socketUDPv4, (struct sockaddr *)&addr_serv_v4, sizeof(addr_serv_v4)) < 0) {
std::cerr << "searchCamera:: ipv4 bind port " << port << " failed!" << std::endl;
} else {
bindv4Success = true;
}
}
if (!bindv6Success) {
addr_serv_v6.sin6_port = htons(port);
if (bind(socketUDPv6, (struct sockaddr *)&addr_serv_v6, sizeof(addr_serv_v6)) < 0) {
std::cerr << "searchCamera:: ipv6 bind port " << port << " failed!" << std::endl;
} else {
bindv6Success = true;
}
}
if (bindv4Success && bindv6Success) {
break;
}
}
if (!(bindv4Success && bindv6Success)) {
return false;
}
ip_mreq mreq_v4;
mreq_v4.imr_multiaddr.s_addr = inet_addr("224.0.0.251");
mreq_v4.imr_interface.s_addr = htonl(INADDR_ANY);
if (setsockopt(socketUDPv4, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mreq_v4, sizeof(mreq_v4)) < 0) {
std::cerr << "searchCamera:: failed to join multicast group for IPv4!" << std::endl;
return false;
}
ipv6_mreq mreq_v6;
inet_pton(AF_INET6, "ff02::fb", &mreq_v6.ipv6mr_multiaddr);
mreq_v6.ipv6mr_interface = 0; // Use the default interface
if (setsockopt(socketUDPv6, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char *)&mreq_v6, sizeof(mreq_v6)) < 0) {
std::cerr << "searchCamera:: failed to join multicast group for IPv6!" << std::endl;
return false;
}
int recv_num_v4, recv_num_v6;
char recvBuf_v4[1025], recvBuf_v6[1025];
std::vector<std::string> ipList;
struct sockaddr_in addr_client_v4;
struct sockaddr_in6 addr_client_v6;
auto pushBackIfNotExists = [&](const std::string& infoStr) {
nlohmann::json infoJson = nlohmann::json::parse(infoStr);
if (infoJson.contains(JSON_KEY_SN) &&
infoJson.contains(JSON_KEY_IP)) {
EpicEyeInfo info;
info.SN = infoJson[JSON_KEY_SN];
info.IP = infoJson[JSON_KEY_IP];
bool exists = std::any_of(cameraList.begin(), cameraList.end(), [&](const EpicEyeInfo& existingInfo) {
return existingInfo.IP == info.IP;
});
if (!exists) {
cameraList.push_back(info);
}
}
};
for (int i = 0; i < 6; i++) {
recv_num_v4 = recvfrom(socketUDPv4, recvBuf_v4, sizeof(recvBuf_v4) - 1, 0,
(struct sockaddr *)&addr_client_v4, (socklen_t *)&len_v4);
if (recv_num_v4 > 0) {
char addressArray[INET_ADDRSTRLEN] = {'\0'};
inet_ntop(AF_INET, &addr_client_v4.sin_addr, addressArray, sizeof(addressArray));
std::string peerIP = addressArray;
recvBuf_v4[recv_num_v4] = '\0';
std::string infoStr = (std::string)recvBuf_v4;
pushBackIfNotExists(infoStr);
}
recv_num_v6 = recvfrom(socketUDPv6, recvBuf_v6, sizeof(recvBuf_v6) - 1, 0,
(struct sockaddr *)&addr_client_v6, (socklen_t *)&len_v6);
if (recv_num_v6 > 0) {
char addressArray[INET6_ADDRSTRLEN] = {'\0'};
inet_ntop(AF_INET6, &addr_client_v6.sin6_addr, addressArray, sizeof(addressArray));
std::string peerIP = addressArray;
recvBuf_v6[recv_num_v6] = '\0';
std::string infoStr = (std::string)recvBuf_v6;
pushBackIfNotExists(infoStr);
}
}
#ifdef _WIN32
closesocket(socketUDPv4);
closesocket(socketUDPv6);
#else
close(socketUDPv4);
close(socketUDPv6);
#endif
if (cameraList.size() == 0) {
return false;
}
return true;
}
} // namespace TFTech