x
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
build
|
||||
61
CMakeLists.txt
Executable file
61
CMakeLists.txt
Executable file
@ -0,0 +1,61 @@
|
||||
CMAKE_MINIMUM_REQUIRED(VERSION 3.5)
|
||||
PROJECT(ZED_BodyFusion)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
SET(CMAKE_BUILD_TYPE "Release")
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
option(LINK_SHARED_ZED "Link with the ZED SDK shared executable" ON)
|
||||
|
||||
if (NOT LINK_SHARED_ZED AND MSVC)
|
||||
message(FATAL_ERROR "LINK_SHARED_ZED OFF : ZED SDK static libraries not available on Windows")
|
||||
endif()
|
||||
|
||||
find_package(ZED REQUIRED)
|
||||
find_package(CUDA REQUIRED)
|
||||
find_package(GLUT REQUIRED)
|
||||
find_package(GLEW REQUIRED)
|
||||
SET(OpenGL_GL_PREFERENCE GLVND)
|
||||
find_package(OpenGL REQUIRED)
|
||||
find_package(OpenCV)
|
||||
|
||||
include_directories(${CUDA_INCLUDE_DIRS})
|
||||
include_directories(${ZED_INCLUDE_DIRS})
|
||||
include_directories(${GLEW_INCLUDE_DIRS})
|
||||
include_directories(${GLUT_INCLUDE_DIR})
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
include_directories(${OpenCV_INCLUDE_DIRS})
|
||||
|
||||
link_directories(${ZED_LIBRARY_DIR})
|
||||
link_directories(${CUDA_LIBRARY_DIRS})
|
||||
link_directories(${GLEW_LIBRARY_DIRS})
|
||||
link_directories(${GLUT_LIBRARY_DIRS})
|
||||
link_directories(${OpenGL_LIBRARY_DIRS})
|
||||
link_directories(${OpenCV_LIBRARY_DIRS})
|
||||
|
||||
IF(NOT WIN32)
|
||||
SET(SPECIAL_OS_LIBS "pthread")
|
||||
|
||||
IF (CMAKE_SYSTEM_PROCESSOR MATCHES aarch64)
|
||||
add_definitions(-DJETSON_STYLE)
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
|
||||
FILE(GLOB_RECURSE SRC_FILES src/*.c*)
|
||||
FILE(GLOB_RECURSE HDR_FILES include/*.h*)
|
||||
|
||||
add_executable(${PROJECT_NAME} ${HDR_FILES} ${SRC_FILES})
|
||||
|
||||
if (LINK_SHARED_ZED)
|
||||
SET(ZED_LIBS ${ZED_LIBRARIES} ${CUDA_CUDA_LIBRARY} ${CUDA_CUDART_LIBRARY})
|
||||
else()
|
||||
SET(ZED_LIBS ${ZED_STATIC_LIBRARIES} ${CUDA_CUDA_LIBRARY} ${CUDA_LIBRARY})
|
||||
endif()
|
||||
|
||||
TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${ZED_LIBS} ${UTILS_LIB} ${SPECIAL_OS_LIBS} ${OpenCV_LIBRARIES} ${OPENGL_LIBRARIES} ${GLUT_LIBRARIES} ${GLEW_LIBRARIES})
|
||||
|
||||
if(INSTALL_SAMPLES)
|
||||
LIST(APPEND SAMPLE_LIST ${PROJECT_NAME})
|
||||
SET(SAMPLE_LIST "${SAMPLE_LIST}" PARENT_SCOPE)
|
||||
endif()
|
||||
349
ZED_SDK_ARCHITECTURE.md
Normal file
349
ZED_SDK_ARCHITECTURE.md
Normal file
@ -0,0 +1,349 @@
|
||||
# ZED SDK Architecture: Streaming vs Fusion API
|
||||
|
||||
## Overview
|
||||
|
||||
The ZED SDK provides two distinct APIs for transmitting camera data over a network:
|
||||
|
||||
1. **Streaming API** (`enableStreaming`) - Video streaming
|
||||
2. **Fusion API** (`startPublishing`) - Metadata publishing
|
||||
|
||||
These serve fundamentally different use cases and have different compute/bandwidth tradeoffs.
|
||||
|
||||
## API Comparison
|
||||
|
||||
| Feature | Streaming API | Fusion API |
|
||||
|---------|---------------|------------|
|
||||
| **Primary Use Case** | Remote camera access | Multi-camera data fusion |
|
||||
| **Data Transmitted** | Compressed video (H264/H265) | Metadata only (bodies, objects, poses) |
|
||||
| **Bandwidth per Camera** | 10-40 Mbps | <100 Kbps |
|
||||
| **Edge Compute** | Video encoding only (NVENC) | Full depth NN + tracking + detection |
|
||||
| **Host Compute** | Full depth NN + tracking + detection | Lightweight fusion only |
|
||||
| **Synchronization** | None | Time-synced + geometric calibration |
|
||||
| **360° Coverage** | No | Yes (fuses overlapping views) |
|
||||
| **Receiver API** | `zed.open()` with `INPUT_TYPE::STREAM` | `fusion.subscribe()` |
|
||||
|
||||
## Architecture Diagrams
|
||||
|
||||
### Streaming API (Single Camera Remote Access)
|
||||
|
||||
```
|
||||
┌─────────────────────┐ ┌─────────────────────┐
|
||||
│ Edge (Jetson) │ │ Host (Server) │
|
||||
│ │ │ │
|
||||
│ ┌───────────────┐ │ H264/H265 RTP │ ┌───────────────┐ │
|
||||
│ │ ZED Camera │ │ (10-40 Mbps) │ │ Decode │ │
|
||||
│ └───────┬───────┘ │ ───────────────────► │ │ (NVENC) │ │
|
||||
│ │ │ │ └───────┬───────┘ │
|
||||
│ ┌───────▼───────┐ │ │ │ │
|
||||
│ │ NVENC │ │ │ ┌───────▼───────┐ │
|
||||
│ │ Encode │──┘ │ │ Neural Depth │ │
|
||||
│ │ (hardware) │ │ │ (NN on GPU) │ │
|
||||
│ └───────────────┘ │ └───────┬───────┘ │
|
||||
│ │ │ │
|
||||
│ │ ┌───────▼───────┐ │
|
||||
│ │ │ Tracking / │ │
|
||||
│ │ │ Detection │ │
|
||||
│ │ └───────┬───────┘ │
|
||||
│ │ │ │
|
||||
│ │ ┌───────▼───────┐ │
|
||||
│ │ │ Point Cloud │ │
|
||||
│ │ └───────────────┘ │
|
||||
└─────────────────────┘ └─────────────────────┘
|
||||
|
||||
Edge: Lightweight (encode only)
|
||||
Host: Heavy (NN depth + all processing)
|
||||
```
|
||||
|
||||
### Fusion API (Multi-Camera 360° Coverage)
|
||||
|
||||
```
|
||||
┌─────────────────────┐
|
||||
│ Edge #1 (Jetson) │
|
||||
│ ┌───────────────┐ │
|
||||
│ │ ZED Camera │ │
|
||||
│ └───────┬───────┘ │ Metadata Only
|
||||
│ ┌───────▼───────┐ │ (bodies, poses)
|
||||
│ │ Neural Depth │ │ (<100 Kbps) ┌─────────────────────┐
|
||||
│ │ (NN on GPU) │ │ ──────────────────────►│ │
|
||||
│ └───────┬───────┘ │ │ Fusion Server │
|
||||
│ ┌───────▼───────┐ │ │ │
|
||||
│ │ Body Track │──┘ │ ┌───────────────┐ │
|
||||
│ └───────────────┘ │ │ Subscribe │ │
|
||||
└─────────────────────┘ │ │ to all │ │
|
||||
│ │ cameras │ │
|
||||
┌─────────────────────┐ │ └───────┬───────┘ │
|
||||
│ Edge #2 (Jetson) │ │ │ │
|
||||
│ ┌───────────────┐ │ Metadata Only │ ┌───────▼───────┐ │
|
||||
│ │ ZED Camera │ │ ──────────────────────►│ │ Time Sync │ │
|
||||
│ └───────┬───────┘ │ │ │ + Geometric │ │
|
||||
│ ┌───────▼───────┐ │ │ │ Calibration │ │
|
||||
│ │ Neural Depth │ │ │ └───────┬───────┘ │
|
||||
│ └───────┬───────┘ │ │ │ │
|
||||
│ ┌───────▼───────┐ │ │ ┌───────▼───────┐ │
|
||||
│ │ Body Track │──┘ │ │ 360° Fusion │ │
|
||||
│ └───────────────┘ │ │ (merge views)│ │
|
||||
└─────────────────────┘ │ └───────────────┘ │
|
||||
│ │
|
||||
┌─────────────────────┐ │ Lightweight GPU │
|
||||
│ Edge #3 (Jetson) │ Metadata Only │ requirements │
|
||||
│ ... │ ──────────────────────►│ │
|
||||
└─────────────────────┘ └─────────────────────┘
|
||||
|
||||
Each Edge: Heavy (NN depth + tracking)
|
||||
Fusion Server: Lightweight (data fusion only)
|
||||
```
|
||||
|
||||
## Communication Modes
|
||||
|
||||
### Streaming API
|
||||
|
||||
| Mode | Description |
|
||||
|------|-------------|
|
||||
| **H264** | AVCHD encoding, wider GPU support |
|
||||
| **H265** | HEVC encoding, better compression, requires Pascal+ GPU |
|
||||
|
||||
Port: Even number (default 30000), uses RTP protocol.
|
||||
|
||||
### Fusion API
|
||||
|
||||
| Mode | Description |
|
||||
|------|-------------|
|
||||
| **INTRA_PROCESS** | Same machine, shared memory (zero-copy) |
|
||||
| **LOCAL_NETWORK** | Different machines, RTP over network |
|
||||
|
||||
Port: Default 30000, configurable per camera.
|
||||
|
||||
## Bandwidth Requirements
|
||||
|
||||
### Streaming (H265 Compressed Video)
|
||||
|
||||
| Resolution | FPS | Bitrate per Camera | 4 Cameras |
|
||||
|------------|-----|-------------------|-----------|
|
||||
| 2K | 15 | 7 Mbps | 28 Mbps |
|
||||
| HD1080 | 30 | 11 Mbps | 44 Mbps |
|
||||
| HD720 | 60 | 6 Mbps | 24 Mbps |
|
||||
| HD1200 | 30 | ~12 Mbps | ~48 Mbps |
|
||||
|
||||
### Fusion (Metadata Only)
|
||||
|
||||
| Data Type | Size per Frame | @ 30 FPS | 4 Cameras |
|
||||
|-----------|---------------|----------|-----------|
|
||||
| Body (18 keypoints) | ~2 KB | ~60 KB/s | ~240 KB/s |
|
||||
| Object detection | ~1 KB | ~30 KB/s | ~120 KB/s |
|
||||
| Pose/Transform | ~100 B | ~3 KB/s | ~12 KB/s |
|
||||
|
||||
**Fusion uses 100-1000x less bandwidth than Streaming.**
|
||||
|
||||
## The Architectural Gap
|
||||
|
||||
### What You CAN Do
|
||||
|
||||
| Scenario | API | Edge Computes | Host Receives |
|
||||
|----------|-----|---------------|---------------|
|
||||
| Remote camera access | Streaming | Video encoding | Video → computes depth/tracking |
|
||||
| Multi-camera fusion | Fusion | Depth + tracking | Metadata only (bodies, poses) |
|
||||
| Local processing | Direct | Everything | N/A (same machine) |
|
||||
|
||||
### What You CANNOT Do
|
||||
|
||||
**There is no ZED SDK mode for:**
|
||||
|
||||
```
|
||||
┌─────────────────────┐ ┌─────────────────────┐
|
||||
│ Edge (Jetson) │ │ Host (Server) │
|
||||
│ │ │ │
|
||||
│ ┌───────────────┐ │ Depth Map / │ ┌───────────────┐ │
|
||||
│ │ ZED Camera │ │ Point Cloud │ │ Receive │ │
|
||||
│ └───────┬───────┘ │ │ │ Depth/PC │ │
|
||||
│ │ │ ??? │ └───────┬───────┘ │
|
||||
│ ┌───────▼───────┐ │ ─────────────────X─► │ │ │
|
||||
│ │ Neural Depth │ │ NOT SUPPORTED │ ┌───────▼───────┐ │
|
||||
│ │ (NN on GPU) │ │ │ │ Further │ │
|
||||
│ └───────┬───────┘ │ │ │ Processing │ │
|
||||
│ │ │ │ └───────────────┘ │
|
||||
│ ┌───────▼───────┐ │ │ │
|
||||
│ │ Point Cloud │──┘ │ │
|
||||
│ └───────────────┘ │ │
|
||||
└─────────────────────┘ └─────────────────────┘
|
||||
|
||||
❌ Edge computes depth → streams depth map → Host receives depth
|
||||
❌ Edge computes point cloud → streams point cloud → Host receives point cloud
|
||||
```
|
||||
|
||||
## Why This Architecture?
|
||||
|
||||
### 1. Bandwidth Economics
|
||||
|
||||
Point cloud streaming would require significantly more bandwidth than video:
|
||||
|
||||
| Data Type | Size per Frame (HD1080) | @ 30 FPS |
|
||||
|-----------|------------------------|----------|
|
||||
| Raw stereo video | ~12 MB | 360 MB/s |
|
||||
| H265 compressed | ~46 KB | 11 Mbps |
|
||||
| Depth map (16-bit) | ~4 MB | 120 MB/s |
|
||||
| Point cloud (XYZ float) | ~12 MB | 360 MB/s |
|
||||
|
||||
Compressed depth/point cloud is lossy and still large (~50-100 Mbps).
|
||||
|
||||
### 2. Compute Distribution Philosophy
|
||||
|
||||
ZED SDK follows: **"Compute entirely at edge OR entirely at host, not split"**
|
||||
|
||||
| Scenario | Solution |
|
||||
|----------|----------|
|
||||
| Low bandwidth, multi-camera | Fusion (edge computes all, sends metadata) |
|
||||
| High bandwidth, single camera | Streaming (host computes all) |
|
||||
| Same machine | INTRA_PROCESS (shared memory) |
|
||||
|
||||
### 3. Fusion API Design Goals
|
||||
|
||||
From Stereolabs documentation:
|
||||
|
||||
> "The Fusion module is **lightweight** (in computation resources requirements) compared to the requirements for camera publishers."
|
||||
|
||||
The Fusion receiver is intentionally lightweight because:
|
||||
- It only needs to fuse pre-computed metadata
|
||||
- It handles time synchronization and geometric calibration
|
||||
- It can run on modest hardware while edges do heavy compute
|
||||
|
||||
### 4. Product Strategy
|
||||
|
||||
Stereolabs sells:
|
||||
- **ZED cameras** (hardware)
|
||||
- **ZED Box** (edge compute appliances)
|
||||
- **ZED Hub** (cloud management)
|
||||
|
||||
The Fusion API encourages purchasing ZED Boxes for edge compute rather than building custom streaming solutions.
|
||||
|
||||
## Workarounds for Custom Point Cloud Streaming
|
||||
|
||||
If you need to stream point clouds from edge to host (outside ZED SDK):
|
||||
|
||||
### Option 1: Custom Compression + Streaming
|
||||
|
||||
```cpp
|
||||
// On edge: compute point cloud, compress, send
|
||||
sl::Mat point_cloud;
|
||||
zed.retrieveMeasure(point_cloud, MEASURE::XYZRGBA);
|
||||
|
||||
// Compress with Draco/PCL octree
|
||||
std::vector<uint8_t> compressed = draco_compress(point_cloud);
|
||||
|
||||
// Send via ZeroMQ/gRPC/raw UDP
|
||||
socket.send(compressed);
|
||||
```
|
||||
|
||||
### Option 2: Depth Map Streaming
|
||||
|
||||
```cpp
|
||||
// On edge: get depth, compress as 16-bit PNG, send
|
||||
sl::Mat depth;
|
||||
zed.retrieveMeasure(depth, MEASURE::DEPTH);
|
||||
|
||||
// Compress as lossless PNG
|
||||
cv::Mat depth_cv = slMat2cvMat(depth);
|
||||
std::vector<uint8_t> png;
|
||||
cv::imencode(".png", depth_cv, png);
|
||||
|
||||
// Send via network
|
||||
socket.send(png);
|
||||
```
|
||||
|
||||
### Bandwidth Estimate for Custom Streaming
|
||||
|
||||
| Method | Compression | Bandwidth (HD1080@30fps) |
|
||||
|--------|-------------|-------------------------|
|
||||
| Depth PNG (lossless) | ~4:1 | ~240 Mbps |
|
||||
| Depth JPEG (lossy) | ~20:1 | ~48 Mbps |
|
||||
| Point cloud Draco | ~10:1 | ~100 Mbps |
|
||||
|
||||
**10 Gbps Ethernet could handle 4 cameras with custom depth streaming.**
|
||||
|
||||
## Recommendations
|
||||
|
||||
| Use Case | Recommended API |
|
||||
|----------|-----------------|
|
||||
| Single camera, remote development | Streaming |
|
||||
| Multi-camera body tracking | Fusion |
|
||||
| Multi-camera 360° coverage | Fusion |
|
||||
| Custom point cloud pipeline | Manual (ZeroMQ + Draco) |
|
||||
| Low latency, same machine | INTRA_PROCESS |
|
||||
|
||||
## Code Examples
|
||||
|
||||
### Streaming Sender (Edge)
|
||||
|
||||
```cpp
|
||||
sl::StreamingParameters stream_params;
|
||||
stream_params.codec = sl::STREAMING_CODEC::H265;
|
||||
stream_params.bitrate = 12000;
|
||||
stream_params.port = 30000;
|
||||
|
||||
zed.enableStreaming(stream_params);
|
||||
|
||||
while (running) {
|
||||
zed.grab(); // Encodes and sends frame
|
||||
}
|
||||
```
|
||||
|
||||
### Streaming Receiver (Host)
|
||||
|
||||
```cpp
|
||||
sl::InitParameters init_params;
|
||||
init_params.input.setFromStream("192.168.1.100", 30000);
|
||||
|
||||
zed.open(init_params);
|
||||
|
||||
while (running) {
|
||||
if (zed.grab() == ERROR_CODE::SUCCESS) {
|
||||
// Full ZED SDK available - depth, tracking, etc.
|
||||
zed.retrieveMeasure(depth, MEASURE::DEPTH);
|
||||
zed.retrieveMeasure(point_cloud, MEASURE::XYZRGBA);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Fusion Sender (Edge)
|
||||
|
||||
```cpp
|
||||
// Enable body tracking
|
||||
zed.enableBodyTracking(body_params);
|
||||
|
||||
// Start publishing metadata
|
||||
sl::CommunicationParameters comm_params;
|
||||
comm_params.setForLocalNetwork(30000);
|
||||
zed.startPublishing(comm_params);
|
||||
|
||||
while (running) {
|
||||
if (zed.grab() == ERROR_CODE::SUCCESS) {
|
||||
zed.retrieveBodies(bodies); // Computes and publishes
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Fusion Receiver (Host)
|
||||
|
||||
```cpp
|
||||
sl::Fusion fusion;
|
||||
fusion.init(init_fusion_params);
|
||||
|
||||
sl::CameraIdentifier cam1(serial_number);
|
||||
fusion.subscribe(cam1, comm_params, pose);
|
||||
|
||||
while (running) {
|
||||
if (fusion.process() == FUSION_ERROR_CODE::SUCCESS) {
|
||||
fusion.retrieveBodies(fused_bodies); // Already computed by edges
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
The ZED SDK architecture forces a choice:
|
||||
|
||||
1. **Streaming**: Edge sends video → Host computes depth (NN inference on host)
|
||||
2. **Fusion**: Edge computes depth → Sends metadata only (no point cloud)
|
||||
|
||||
There is **no built-in support** for streaming computed depth maps or point clouds from edge to host. This is by design for bandwidth efficiency and to encourage use of ZED Box edge compute products.
|
||||
|
||||
For custom depth/point cloud streaming, you must implement your own compression and network layer outside the ZED SDK.
|
||||
74
include/ClientPublisher.hpp
Executable file
74
include/ClientPublisher.hpp
Executable file
@ -0,0 +1,74 @@
|
||||
#ifndef __SENDER_RUNNER_HDR__
|
||||
#define __SENDER_RUNNER_HDR__
|
||||
|
||||
#include <sl/Camera.hpp>
|
||||
#include <sl/Fusion.hpp>
|
||||
|
||||
#include <thread>
|
||||
#include <condition_variable>
|
||||
|
||||
// Hard-coded camera stream configuration from inside_network.json
|
||||
struct CameraStreamConfig {
|
||||
int serial_number;
|
||||
std::string ip_address;
|
||||
int port;
|
||||
};
|
||||
|
||||
// Predefined camera stream configurations
|
||||
const std::vector<CameraStreamConfig> CAMERA_STREAM_CONFIGS = {
|
||||
{44289123, "192.168.128.2", 30000},
|
||||
{44435674, "192.168.128.2", 30002},
|
||||
{41831756, "192.168.128.2", 30004},
|
||||
{46195029, "192.168.128.2", 30006}
|
||||
};
|
||||
|
||||
struct Trigger{
|
||||
|
||||
void notifyZED(){
|
||||
|
||||
cv.notify_all();
|
||||
|
||||
if(running){
|
||||
bool wait_for_zed = true;
|
||||
const int nb_zed = states.size();
|
||||
while(wait_for_zed){
|
||||
int count_r = 0;
|
||||
for(auto &it:states)
|
||||
count_r += it.second;
|
||||
wait_for_zed = count_r != nb_zed;
|
||||
sl::sleep_ms(1);
|
||||
}
|
||||
for(auto &it:states)
|
||||
it.second = false;
|
||||
}
|
||||
}
|
||||
|
||||
std::condition_variable cv;
|
||||
bool running = true;
|
||||
std::map<int, bool> states;
|
||||
};
|
||||
|
||||
class ClientPublisher{
|
||||
|
||||
public:
|
||||
ClientPublisher();
|
||||
~ClientPublisher();
|
||||
|
||||
// Open camera from stream (IP:port) instead of local input
|
||||
bool open(const std::string& ip, int port, Trigger* ref, int sdk_gpu_id);
|
||||
void start();
|
||||
void stop();
|
||||
void setStartSVOPosition(unsigned pos);
|
||||
|
||||
int getSerialNumber() const { return serial; }
|
||||
|
||||
private:
|
||||
sl::Camera zed;
|
||||
void work();
|
||||
std::thread runner;
|
||||
int serial;
|
||||
std::mutex mtx;
|
||||
Trigger *p_trigger;
|
||||
};
|
||||
|
||||
#endif // ! __SENDER_RUNNER_HDR__
|
||||
336
include/GLViewer.hpp
Executable file
336
include/GLViewer.hpp
Executable file
@ -0,0 +1,336 @@
|
||||
#ifndef __VIEWER_INCLUDE__
|
||||
#define __VIEWER_INCLUDE__
|
||||
|
||||
#include <random>
|
||||
|
||||
#include <sl/Camera.hpp>
|
||||
#include <sl/Fusion.hpp>
|
||||
|
||||
#include <GL/glew.h>
|
||||
#include <GL/freeglut.h>
|
||||
|
||||
#include <cuda.h>
|
||||
#include <cuda_gl_interop.h>
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.141592653f
|
||||
#endif
|
||||
|
||||
#define MOUSE_R_SENSITIVITY 0.03f
|
||||
#define MOUSE_UZ_SENSITIVITY 0.75f
|
||||
#define MOUSE_DZ_SENSITIVITY 1.25f
|
||||
#define MOUSE_T_SENSITIVITY 0.05f
|
||||
#define KEY_T_SENSITIVITY 0.1f
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class Shader {
|
||||
public:
|
||||
|
||||
Shader() : verterxId_(0), fragmentId_(0), programId_(0) {}
|
||||
Shader(const GLchar* vs, const GLchar* fs);
|
||||
~Shader();
|
||||
|
||||
// Delete the move constructor and move assignment operator
|
||||
Shader(Shader&&) = delete;
|
||||
Shader& operator=(Shader&&) = delete;
|
||||
|
||||
// Delete the copy constructor and copy assignment operator
|
||||
Shader(const Shader&) = delete;
|
||||
Shader& operator=(const Shader&) = delete;
|
||||
|
||||
void set(const GLchar* vs, const GLchar* fs);
|
||||
GLuint getProgramId();
|
||||
|
||||
static const GLint ATTRIB_VERTICES_POS = 0;
|
||||
static const GLint ATTRIB_COLOR_POS = 1;
|
||||
static const GLint ATTRIB_NORMAL = 2;
|
||||
private:
|
||||
bool compile(GLuint &shaderId, GLenum type, const GLchar* src);
|
||||
GLuint verterxId_;
|
||||
GLuint fragmentId_;
|
||||
GLuint programId_;
|
||||
};
|
||||
|
||||
struct ShaderData {
|
||||
Shader it;
|
||||
GLuint MVP_Mat;
|
||||
};
|
||||
|
||||
class Simple3DObject {
|
||||
public:
|
||||
|
||||
Simple3DObject();
|
||||
|
||||
~Simple3DObject();
|
||||
|
||||
void addPoint(sl::float3 pt, sl::float3 clr);
|
||||
void addLine(sl::float3 pt1, sl::float3 pt2, sl::float3 clr);
|
||||
void addFace(sl::float3 p1, sl::float3 p2, sl::float3 p3, sl::float3 clr);
|
||||
void pushToGPU();
|
||||
void clear();
|
||||
|
||||
void setStatic(bool _static) {
|
||||
isStatic_ = _static;
|
||||
}
|
||||
|
||||
void setDrawingType(GLenum type);
|
||||
|
||||
void draw();
|
||||
|
||||
private:
|
||||
std::vector<sl::float3> vertices_;
|
||||
std::vector<sl::float3> colors_;
|
||||
std::vector<unsigned int> indices_;
|
||||
|
||||
bool isStatic_;
|
||||
bool need_update;
|
||||
GLenum drawingType_;
|
||||
GLuint vaoID_;
|
||||
GLuint vboID_[3];
|
||||
};
|
||||
|
||||
class CameraGL {
|
||||
public:
|
||||
|
||||
CameraGL() {
|
||||
}
|
||||
|
||||
enum DIRECTION {
|
||||
UP, DOWN, LEFT, RIGHT, FORWARD, BACK
|
||||
};
|
||||
CameraGL(sl::Translation position, sl::Translation direction, sl::Translation vertical = sl::Translation(0, 1, 0)); // vertical = Eigen::Vector3f(0, 1, 0)
|
||||
~CameraGL();
|
||||
|
||||
void update();
|
||||
void setProjection(float horizontalFOV, float verticalFOV, float znear, float zfar);
|
||||
const sl::Transform& getViewProjectionMatrix() const;
|
||||
|
||||
float getHorizontalFOV() const;
|
||||
float getVerticalFOV() const;
|
||||
|
||||
// Set an offset between the eye of the camera and its position
|
||||
// Note: Useful to use the camera as a trackball camera with z>0 and x = 0, y = 0
|
||||
// Note: coordinates are in local space
|
||||
void setOffsetFromPosition(const sl::Translation& offset);
|
||||
const sl::Translation& getOffsetFromPosition() const;
|
||||
|
||||
void setDirection(const sl::Translation& direction, const sl::Translation &vertical);
|
||||
void translate(const sl::Translation& t);
|
||||
void setPosition(const sl::Translation& p);
|
||||
void rotate(const sl::Orientation& rot);
|
||||
void rotate(const sl::Rotation& m);
|
||||
void setRotation(const sl::Orientation& rot);
|
||||
void setRotation(const sl::Rotation& m);
|
||||
|
||||
const sl::Translation& getPosition() const;
|
||||
const sl::Translation& getForward() const;
|
||||
const sl::Translation& getRight() const;
|
||||
const sl::Translation& getUp() const;
|
||||
const sl::Translation& getVertical() const;
|
||||
float getZNear() const;
|
||||
float getZFar() const;
|
||||
|
||||
static const sl::Translation ORIGINAL_FORWARD;
|
||||
static const sl::Translation ORIGINAL_UP;
|
||||
static const sl::Translation ORIGINAL_RIGHT;
|
||||
|
||||
sl::Transform projection_;
|
||||
bool usePerspective_;
|
||||
private:
|
||||
void updateVectors();
|
||||
void updateView();
|
||||
void updateVPMatrix();
|
||||
|
||||
sl::Translation offset_;
|
||||
sl::Translation position_;
|
||||
sl::Translation forward_;
|
||||
sl::Translation up_;
|
||||
sl::Translation right_;
|
||||
sl::Translation vertical_;
|
||||
|
||||
sl::Orientation rotation_;
|
||||
|
||||
sl::Transform view_;
|
||||
sl::Transform vpMatrix_;
|
||||
float horizontalFieldOfView_;
|
||||
float verticalFieldOfView_;
|
||||
float znear_;
|
||||
float zfar_;
|
||||
};
|
||||
|
||||
|
||||
class PointCloud {
|
||||
public:
|
||||
PointCloud();
|
||||
~PointCloud();
|
||||
|
||||
// Initialize Opengl and Cuda buffers
|
||||
// Warning: must be called in the Opengl thread
|
||||
void initialize(sl::Mat&, sl::float3 clr);
|
||||
// Push a new point cloud
|
||||
// Warning: can be called from any thread but the mutex "mutexData" must be locked
|
||||
void pushNewPC();
|
||||
// Draw the point cloud
|
||||
// Warning: must be called in the Opengl thread
|
||||
void draw(const sl::Transform& vp, bool draw_clr);
|
||||
// Close (disable update)
|
||||
void close();
|
||||
|
||||
private:
|
||||
sl::Mat refMat;
|
||||
sl::float3 clr;
|
||||
|
||||
Shader shader_;
|
||||
GLuint shMVPMatrixLoc_;
|
||||
GLuint shDrawColor;
|
||||
GLuint shColor;
|
||||
|
||||
size_t numBytes_;
|
||||
float* xyzrgbaMappedBuf_;
|
||||
GLuint bufferGLID_;
|
||||
cudaGraphicsResource* bufferCudaID_;
|
||||
};
|
||||
|
||||
class CameraViewer {
|
||||
public:
|
||||
CameraViewer();
|
||||
~CameraViewer();
|
||||
|
||||
// Initialize Opengl and Cuda buffers
|
||||
bool initialize(sl::Mat& image, sl::float3 clr);
|
||||
// Push a new Image + Z buffer and transform into a point cloud
|
||||
void pushNewImage();
|
||||
// Draw the Image
|
||||
void draw(sl::Transform vpMatrix);
|
||||
// Close (disable update)
|
||||
void close();
|
||||
|
||||
Simple3DObject frustum;
|
||||
private:
|
||||
sl::Mat ref;
|
||||
cudaArray_t ArrIm;
|
||||
cudaGraphicsResource* cuda_gl_ressource;//cuda GL resource
|
||||
Shader shader;
|
||||
GLuint shMVPMatrixLocTex_;
|
||||
|
||||
GLuint texture;
|
||||
GLuint vaoID_;
|
||||
GLuint vboID_[3];
|
||||
|
||||
std::vector<sl::uint3> faces;
|
||||
std::vector<sl::float3> vert;
|
||||
std::vector<sl::float2> uv;
|
||||
};
|
||||
|
||||
struct ObjectClassName {
|
||||
sl::float3 position;
|
||||
std::string name_lineA;
|
||||
std::string name_lineB;
|
||||
sl::float3 color;
|
||||
};
|
||||
|
||||
// This class manages input events, window and Opengl rendering pipeline
|
||||
|
||||
class GLViewer {
|
||||
public:
|
||||
GLViewer();
|
||||
~GLViewer();
|
||||
bool isAvailable();
|
||||
|
||||
void init(int argc, char **argv);
|
||||
|
||||
void updateCamera(int, sl::Mat &, sl::Mat &);
|
||||
void updateCamera(sl::Mat &);
|
||||
|
||||
void updateBodies(sl::Bodies &objs,std::map<sl::CameraIdentifier, sl::Bodies>& singledata, sl::FusionMetrics& metrics);
|
||||
|
||||
void setCameraPose(int, sl::Transform);
|
||||
|
||||
int getKey() {
|
||||
const int key = last_key;
|
||||
last_key = -1;
|
||||
return key;
|
||||
}
|
||||
|
||||
void exit();
|
||||
private:
|
||||
void render();
|
||||
void update();
|
||||
void draw();
|
||||
void clearInputs();
|
||||
void setRenderCameraProjection(sl::CameraParameters params, float znear, float zfar);
|
||||
|
||||
void printText();
|
||||
|
||||
// Glut functions callbacks
|
||||
static void drawCallback();
|
||||
static void mouseButtonCallback(int button, int state, int x, int y);
|
||||
static void mouseMotionCallback(int x, int y);
|
||||
static void reshapeCallback(int width, int height);
|
||||
static void keyPressedCallback(unsigned char c, int x, int y);
|
||||
static void keyReleasedCallback(unsigned char c, int x, int y);
|
||||
static void idle();
|
||||
|
||||
sl::float3 getColor(int, bool);
|
||||
|
||||
bool available;
|
||||
bool drawBbox = false;
|
||||
|
||||
enum MOUSE_BUTTON {
|
||||
LEFT = 0,
|
||||
MIDDLE = 1,
|
||||
RIGHT = 2,
|
||||
WHEEL_UP = 3,
|
||||
WHEEL_DOWN = 4
|
||||
};
|
||||
|
||||
enum KEY_STATE {
|
||||
UP = 'u',
|
||||
DOWN = 'd',
|
||||
FREE = 'f'
|
||||
};
|
||||
|
||||
unsigned char lastPressedKey;
|
||||
|
||||
bool mouseButton_[3];
|
||||
int mouseWheelPosition_;
|
||||
int mouseCurrentPosition_[2];
|
||||
int mouseMotion_[2];
|
||||
int previousMouseMotion_[2];
|
||||
KEY_STATE keyStates_[256];
|
||||
|
||||
std::mutex mtx;
|
||||
std::mutex mtx_clr;
|
||||
|
||||
ShaderData shader;
|
||||
|
||||
sl::Transform projection_;
|
||||
sl::float3 bckgrnd_clr;
|
||||
|
||||
std::map<int, PointCloud> point_clouds;
|
||||
std::map<int, CameraViewer> viewers;
|
||||
std::map<int, sl::Transform> poses;
|
||||
|
||||
std::map<int, Simple3DObject> skeletons_raw;
|
||||
std::map<int, sl::float3> colors;
|
||||
std::map<int, sl::float3> colors_sk;
|
||||
|
||||
std::vector<ObjectClassName> fusionStats;
|
||||
|
||||
CameraGL camera_;
|
||||
Simple3DObject skeletons;
|
||||
Simple3DObject floor_grid;
|
||||
|
||||
bool show_pc = true;
|
||||
bool show_raw = false;
|
||||
bool draw_flat_color = false;
|
||||
|
||||
std::uniform_int_distribution<uint16_t> uint_dist360;
|
||||
std::mt19937 rng;
|
||||
|
||||
int last_key = -1;
|
||||
};
|
||||
|
||||
#endif /* __VIEWER_INCLUDE__ */
|
||||
56
include/utils.hpp
Executable file
56
include/utils.hpp
Executable file
@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include <sl/Camera.hpp>
|
||||
|
||||
/**
|
||||
* @brief Compute the start frame of each SVO for playback to be synced
|
||||
*
|
||||
* @param svo_files Map camera index to SVO file path
|
||||
* @return Map camera index to starting SVO frame for synced playback
|
||||
*/
|
||||
std::map<int, int> syncDATA(std::map<int, std::string> svo_files) {
|
||||
std::map<int, int> output; // map of camera index and frame index of the starting point for each
|
||||
|
||||
// Open all SVO
|
||||
std::map<int, std::shared_ptr<sl::Camera>> p_zeds;
|
||||
|
||||
for (auto &it : svo_files) {
|
||||
auto p_zed = std::make_shared<sl::Camera>();
|
||||
|
||||
sl::InitParameters init_param;
|
||||
init_param.depth_mode = sl::DEPTH_MODE::NONE;
|
||||
init_param.camera_disable_self_calib = true;
|
||||
init_param.input.setFromSVOFile(it.second.c_str());
|
||||
|
||||
auto error = p_zed->open(init_param);
|
||||
if (error == sl::ERROR_CODE::SUCCESS)
|
||||
p_zeds.insert(std::make_pair(it.first, p_zed));
|
||||
else {
|
||||
std::cerr << "Could not open file " << it.second.c_str() << ": " << sl::toString(error) << ". Skipping" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the starting point, we have to take the latest one
|
||||
sl::Timestamp start_ts = 0;
|
||||
for (auto &it : p_zeds) {
|
||||
it.second->grab();
|
||||
auto ts = it.second->getTimestamp(sl::TIME_REFERENCE::IMAGE);
|
||||
|
||||
if (ts > start_ts)
|
||||
start_ts = ts;
|
||||
}
|
||||
|
||||
std::cout << "Found SVOs common starting time: " << start_ts << std::endl;
|
||||
|
||||
// The starting point is now known, let's find the frame idx for all corresponding
|
||||
for (auto &it : p_zeds) {
|
||||
auto frame_position_at_ts = it.second->getSVOPositionAtTimestamp(start_ts);
|
||||
|
||||
if (frame_position_at_ts != -1)
|
||||
output.insert(std::make_pair(it.first, frame_position_at_ts));
|
||||
}
|
||||
|
||||
for (auto &it : p_zeds) it.second->close();
|
||||
|
||||
return output;
|
||||
}
|
||||
104
src/ClientPublisher.cpp
Executable file
104
src/ClientPublisher.cpp
Executable file
@ -0,0 +1,104 @@
|
||||
#include "ClientPublisher.hpp"
|
||||
|
||||
ClientPublisher::ClientPublisher() { }
|
||||
|
||||
ClientPublisher::~ClientPublisher()
|
||||
{
|
||||
zed.close();
|
||||
}
|
||||
|
||||
bool ClientPublisher::open(const std::string& ip, int port, Trigger* ref, int sdk_gpu_id) {
|
||||
|
||||
p_trigger = ref;
|
||||
|
||||
sl::InitParameters init_parameters;
|
||||
init_parameters.depth_mode = sl::DEPTH_MODE::NEURAL_PLUS;
|
||||
// Use stream input instead of local camera
|
||||
init_parameters.input.setFromStream(sl::String(ip.c_str()), port);
|
||||
init_parameters.coordinate_units = sl::UNIT::METER;
|
||||
init_parameters.depth_stabilization = 30;
|
||||
init_parameters.sdk_gpu_id = sdk_gpu_id;
|
||||
|
||||
auto state = zed.open(init_parameters);
|
||||
if (state != sl::ERROR_CODE::SUCCESS)
|
||||
{
|
||||
std::cout << "Error opening stream " << ip << ":" << port << " - " << state << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
serial = zed.getCameraInformation().serial_number;
|
||||
p_trigger->states[serial] = false;
|
||||
|
||||
// in most cases in body tracking setup, the cameras are static
|
||||
sl::PositionalTrackingParameters positional_tracking_parameters;
|
||||
// in most cases for body detection application the camera is static:
|
||||
positional_tracking_parameters.set_as_static = true;
|
||||
|
||||
state = zed.enablePositionalTracking(positional_tracking_parameters);
|
||||
if (state != sl::ERROR_CODE::SUCCESS)
|
||||
{
|
||||
std::cout << "Error enabling positional tracking: " << state << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// define the body tracking parameters, as the fusion can does the tracking and fitting you don't need to enable them here, unless you need it for your app
|
||||
sl::BodyTrackingParameters body_tracking_parameters;
|
||||
body_tracking_parameters.detection_model = sl::BODY_TRACKING_MODEL::HUMAN_BODY_ACCURATE;
|
||||
body_tracking_parameters.body_format = sl::BODY_FORMAT::BODY_18;
|
||||
body_tracking_parameters.enable_body_fitting = false;
|
||||
body_tracking_parameters.enable_tracking = false;
|
||||
state = zed.enableBodyTracking(body_tracking_parameters);
|
||||
if (state != sl::ERROR_CODE::SUCCESS)
|
||||
{
|
||||
std::cout << "Error enabling body tracking: " << state << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ClientPublisher::start()
|
||||
{
|
||||
if (zed.isOpened()) {
|
||||
// the camera should stream its data so the fusion can subscibe to it to gather the detected body and others metadata needed for the process.
|
||||
zed.startPublishing();
|
||||
// the thread can start to process the camera grab in background
|
||||
runner = std::thread(&ClientPublisher::work, this);
|
||||
}
|
||||
}
|
||||
|
||||
void ClientPublisher::stop()
|
||||
{
|
||||
if (runner.joinable())
|
||||
runner.join();
|
||||
zed.close();
|
||||
}
|
||||
|
||||
void ClientPublisher::work()
|
||||
{
|
||||
sl::Bodies bodies;
|
||||
sl::BodyTrackingRuntimeParameters body_runtime_parameters;
|
||||
body_runtime_parameters.detection_confidence_threshold = 40;
|
||||
zed.setBodyTrackingRuntimeParameters(body_runtime_parameters);
|
||||
|
||||
sl::RuntimeParameters rt;
|
||||
rt.confidence_threshold = 50;
|
||||
|
||||
// In this sample we use a dummy thread to process the ZED data.
|
||||
// you can replace it by your own application and use the ZED like you use to, retrieve its images, depth, sensors data and so on.
|
||||
// As long as you call the grab method, since the camera is subscribed to fusion it will run the detection and
|
||||
// the camera will be able to seamlessly transmit the data to the fusion module.
|
||||
while (p_trigger->running) {
|
||||
std::unique_lock<std::mutex> lk(mtx);
|
||||
p_trigger->cv.wait(lk);
|
||||
if (p_trigger->running) {
|
||||
if (zed.grab(rt) == sl::ERROR_CODE::SUCCESS) {
|
||||
}
|
||||
}
|
||||
p_trigger->states[serial] = true;
|
||||
}
|
||||
}
|
||||
|
||||
void ClientPublisher::setStartSVOPosition(unsigned pos) {
|
||||
zed.setSVOPosition(pos);
|
||||
}
|
||||
1161
src/GLViewer.cpp
Executable file
1161
src/GLViewer.cpp
Executable file
File diff suppressed because it is too large
Load Diff
211
src/main.cpp
Executable file
211
src/main.cpp
Executable file
@ -0,0 +1,211 @@
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (c) 2025, STEREOLABS.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// ZED include
|
||||
#include "ClientPublisher.hpp"
|
||||
#include "GLViewer.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
#ifdef _SL_JETSON_
|
||||
const bool isJetson = true;
|
||||
#else
|
||||
const bool isJetson = false;
|
||||
#endif
|
||||
|
||||
if (argc != 2) {
|
||||
// this file should be generated by using the tool ZED360
|
||||
std::cout << "Need a Configuration file in input" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Defines the Coordinate system and unit used in this sample
|
||||
constexpr sl::COORDINATE_SYSTEM COORDINATE_SYSTEM =
|
||||
sl::COORDINATE_SYSTEM::RIGHT_HANDED_Y_UP;
|
||||
constexpr sl::UNIT UNIT = sl::UNIT::METER;
|
||||
|
||||
// Read json file containing the configuration of your multicamera setup.
|
||||
auto configurations =
|
||||
sl::readFusionConfigurationFile(argv[1], COORDINATE_SYSTEM, UNIT);
|
||||
|
||||
if (configurations.empty()) {
|
||||
std::cout << "Empty configuration File." << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
Trigger trigger;
|
||||
|
||||
// Open camera streams from hard-coded configuration
|
||||
std::vector<std::unique_ptr<ClientPublisher>> clients;
|
||||
int id_ = 0;
|
||||
int gpu_id = 0;
|
||||
|
||||
int nb_gpu = 0;
|
||||
if (!isJetson)
|
||||
cudaGetDeviceCount(&nb_gpu);
|
||||
|
||||
// Use hard-coded camera stream configurations
|
||||
for (const auto& cam_config : CAMERA_STREAM_CONFIGS) {
|
||||
std::cout << "Try to open stream for ZED " << cam_config.serial_number
|
||||
<< " at " << cam_config.ip_address << ":" << cam_config.port << ".."
|
||||
<< std::flush;
|
||||
|
||||
gpu_id = (nb_gpu == 0) ? 0 : id_ % nb_gpu;
|
||||
|
||||
auto client = std::make_unique<ClientPublisher>();
|
||||
auto state = client->open(cam_config.ip_address, cam_config.port, &trigger, gpu_id);
|
||||
if (!state) {
|
||||
std::cerr << "Could not open stream for ZED: " << cam_config.serial_number
|
||||
<< ". Skipping..." << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
std::cout << ". ready ! (Serial: " << client->getSerialNumber() << ")" << std::endl;
|
||||
|
||||
clients.push_back(std::move(client));
|
||||
id_++;
|
||||
}
|
||||
|
||||
// start camera threads
|
||||
for (auto &it : clients) {
|
||||
it->start();
|
||||
}
|
||||
|
||||
// Now that the ZED camera are running, we need to initialize the fusion
|
||||
// module
|
||||
sl::InitFusionParameters init_params;
|
||||
init_params.coordinate_units = UNIT;
|
||||
init_params.coordinate_system = COORDINATE_SYSTEM;
|
||||
|
||||
sl::Resolution low_res(512, 360);
|
||||
init_params.maximum_working_resolution = low_res;
|
||||
// create and initialize it
|
||||
sl::Fusion fusion;
|
||||
fusion.init(init_params);
|
||||
|
||||
// subscribe to every cameras of the setup to internally gather their data
|
||||
std::vector<sl::CameraIdentifier> cameras;
|
||||
for (auto &it : configurations) {
|
||||
sl::CameraIdentifier uuid(it.serial_number);
|
||||
// to subscribe to a camera you must give its serial number, the way to
|
||||
// communicate with it (shared memory or local network), and its world pose
|
||||
// in the setup.
|
||||
auto state = fusion.subscribe(uuid, it.communication_parameters, it.pose,
|
||||
it.override_gravity);
|
||||
if (state != sl::FUSION_ERROR_CODE::SUCCESS)
|
||||
std::cout << "Unable to subscribe to " << std::to_string(uuid.sn) << " . "
|
||||
<< state << std::endl;
|
||||
else
|
||||
cameras.push_back(uuid);
|
||||
}
|
||||
|
||||
// check that at least one camera is connected
|
||||
if (cameras.empty()) {
|
||||
std::cout << "no connections " << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// as this sample shows how to fuse body detection from the multi camera setup
|
||||
// we enable the Body Tracking module with its options
|
||||
sl::BodyTrackingFusionParameters body_fusion_init_params;
|
||||
body_fusion_init_params.enable_tracking = true;
|
||||
body_fusion_init_params.enable_body_fitting =
|
||||
!isJetson; // skeletons will looks more natural but requires more
|
||||
// computations
|
||||
fusion.enableBodyTracking(body_fusion_init_params);
|
||||
|
||||
// define fusion behavior
|
||||
sl::BodyTrackingFusionRuntimeParameters body_tracking_runtime_parameters;
|
||||
// be sure that the detection skeleton is complete enough
|
||||
body_tracking_runtime_parameters.skeleton_minimum_allowed_keypoints = 7;
|
||||
// we can also want to retrieve skeleton seen by multiple camera, in this case
|
||||
// at least half of them
|
||||
body_tracking_runtime_parameters.skeleton_minimum_allowed_camera =
|
||||
cameras.size() / 2.;
|
||||
body_tracking_runtime_parameters.skeleton_smoothing = 0.1f;
|
||||
|
||||
// creation of a 3D viewer
|
||||
GLViewer viewer;
|
||||
viewer.init(argc, argv);
|
||||
|
||||
std::cout << "Viewer Shortcuts\n"
|
||||
<< "\t- 'q': quit the application\n"
|
||||
<< "\t- 'r': switch on/off for raw skeleton display\n"
|
||||
<< "\t- 'p': switch on/off for live point cloud display\n"
|
||||
<< "\t- 'c': switch on/off point cloud display with raw color\n"
|
||||
<< std::endl;
|
||||
|
||||
// fusion outputs
|
||||
sl::Bodies fused_bodies;
|
||||
std::map<sl::CameraIdentifier, sl::Bodies> camera_raw_data;
|
||||
sl::FusionMetrics metrics;
|
||||
std::map<sl::CameraIdentifier, sl::Mat> views;
|
||||
std::map<sl::CameraIdentifier, sl::Mat> pointClouds;
|
||||
sl::CameraIdentifier fused_camera(0);
|
||||
|
||||
// run the fusion as long as the viewer is available.
|
||||
while (viewer.isAvailable()) {
|
||||
trigger.notifyZED();
|
||||
|
||||
// run the fusion process (which gather data from all camera, sync them and
|
||||
// process them)
|
||||
if (fusion.process() == sl::FUSION_ERROR_CODE::SUCCESS) {
|
||||
|
||||
// Retrieve fused body
|
||||
fusion.retrieveBodies(fused_bodies, body_tracking_runtime_parameters);
|
||||
// for debug, you can retrieve the data sent by each camera
|
||||
for (auto &id : cameras) {
|
||||
fusion.retrieveBodies(camera_raw_data[id],
|
||||
body_tracking_runtime_parameters, id);
|
||||
|
||||
auto state_view = fusion.retrieveImage(views[id], id, low_res);
|
||||
auto state_pc = fusion.retrieveMeasure(pointClouds[id], id,
|
||||
sl::MEASURE::XYZBGRA, low_res);
|
||||
|
||||
if (state_view == sl::FUSION_ERROR_CODE::SUCCESS &&
|
||||
state_pc == sl::FUSION_ERROR_CODE::SUCCESS)
|
||||
viewer.updateCamera(id.sn, views[id], pointClouds[id]);
|
||||
|
||||
sl::Pose pose;
|
||||
if (fusion.getPosition(pose, sl::REFERENCE_FRAME::WORLD, id,
|
||||
sl::POSITION_TYPE::RAW) ==
|
||||
sl::POSITIONAL_TRACKING_STATE::OK)
|
||||
viewer.setCameraPose(id.sn, pose.pose_data);
|
||||
}
|
||||
|
||||
// get metrics about the fusion process for monitoring purposes
|
||||
fusion.getProcessMetrics(metrics);
|
||||
}
|
||||
// update the 3D view
|
||||
viewer.updateBodies(fused_bodies, camera_raw_data, metrics);
|
||||
}
|
||||
|
||||
trigger.running = false;
|
||||
trigger.notifyZED();
|
||||
|
||||
for (auto &it : clients)
|
||||
it->stop();
|
||||
|
||||
fusion.close();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
Reference in New Issue
Block a user