Skip to content

add new Rest api wrapper #13965

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion wrappers/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@
* [Matlab](./matlab) - Supports 64bit Windows installations
* [OpenNI2](./openni2) - OpenNI2 integration
* [Unreal Engine 4](./unrealengine4) - Unreal Engine 4 integration
* [Rest API](./rest-api) - Rest API wrapper

If you are interested in official support for a language / framework, please let us know via [a new Issue](https://github.com/IntelRealSense/librealsense/issues/new)

## Community Projects:

* Community contributions to add Java wrappers: [Java by edwinRNDR](https://github.com/edwinRNDR/librealsense/tree/master/wrappers/java) and [Java by cansik](https://github.com/cansik/librealsense-java).
* Community contributions to add Java wrappers: [Java by edwinRNDR](https://github.com/edwinRNDR/librealsense/tree/master/wrappers/java) and [Java by cansik](https://github.com/cansik/librealsense-java).
Check the authors' notes: ([edwinRNDR](https://github.com/IntelRealSense/librealsense/issues/1594#issuecomment-429216051)) and [cansik](https://github.com/cansik/librealsense-java#-important).
> A warm **thank you** to [@edwinRNDR](https://github.com/edwinRNDR) and [@cansik](https://github.com/cansik) for further expanding RealSense eco-system!
10 changes: 10 additions & 0 deletions wrappers/rest-api/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Ignore vscode configuration files
.vscode/
.pytest_cache/
# Ignore virtual environment folder
.venv/
venv/

# Ignore Python bytecode files
*.pyc
__pycache__/
87 changes: 87 additions & 0 deletions wrappers/rest-api/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# LibRS Rest API server
## Overview

This Python library provides a convenient wrapper to interact with the [RealSense REST API], a FastAPI-based service that exposes the functionality of the Intel RealSense SDK (librealsense) over a network.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

This was the meaning here or missing some symbol?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just wanted to emphasize it


It simplifies remote control and data streaming from RealSense devices by handling communication protocols for:

1. **RESTful Operations:** For device discovery, configuration, and control.
2. **WebRTC:** For efficient, browser-compatible live video streaming (RGB, Depth).
3. **Socket.IO:** For real-time streaming of frame metadata and point cloud data.
* In order to decode point cloud data in the client, follow those steps:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can add a python or other language you already did it on code snippet?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont have something official I can provide. it just a standard and languege agnostic socketio usage.

* Decode Base64 string to Uint8Array
* 'bytes' now holds the raw byte data that originated from the NumPy array
* Interpret bytes as Float32Array.
IMPORTANT ASSUMPTION: We assume the original NumPy array used float32.
If it used float64 (Python's default float), use Float64Array here.
The ArrayBuffer gives access to the raw binary data buffer of the Uint8Array.
The Float32Array constructor creates a *view* over that buffer, interpreting
groups of 4 bytes as 32-bit floats without copying data.
* Now you can use the 'vertices' Float32Array
* Motion raw data is also propogated using this socket.io channel

## Support Features

* **Device Management:**
* List connected RealSense devices.
* Get detailed information about specific devices (name, serial, firmware, etc.).
* Perform hardware resets remotely.
* **Sensor & Option Control:**
* List available sensors on a device.
* Get detailed sensor information and supported stream profiles.
* List and retrieve current values for sensor options (e.g., exposure, gain).
* Update writable sensor options.
* **REST-based Stream Control:**
* Start/Stop streams with specific configurations (resolution, format, FPS).
* Check the streaming status of a device.
* **WebRTC Video Streaming:**
* Initiate WebRTC sessions with the API to receive live video feeds.
* Handles the offer/answer and ICE candidate exchange mechanisms required for WebRTC setup (via REST endpoints).
* **Socket.IO Data Streaming:**
* Establish a Socket.IO connection to the API.
* Receive real-time events containing:
* Frame metadata (timestamps, frame numbers, etc.).
* Point cloud data streams.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be helpful to mention that all of the endpoints and the supported features are available on the /docs endpoint

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was pretty sure I added it, probably missed that. will add.


## Missing Features
* Not all SDK capabilities are yet supported(e.g. Texture Mapping, Post processing filters,...)

## Installation


1. **Create a virtual environment:**

```bash
python3 -m venv venv
source venv/bin/activate
```

2. **Install dependencies:**

```bash
pip3 install -r requirements.txt
```

3. **Run API server:**

```bash
run start_server.sh
```

4. **Run tests(inside tests folder):**

```bash
pytest test_api_service.py
```


## Testing

1. **To enable testing add the following to the requirements.txt**
```bash
pytest==8.3.5
pytest-asyncio==0.26.0
pyyaml==6.0.2
lark==1.2.2
httpx==0.28.1
```
Empty file.
Empty file.
27 changes: 27 additions & 0 deletions wrappers/rest-api/app/api/dependencies.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from fastapi import Depends
from fastapi.security import OAuth2PasswordBearer
from typing import Generator

from app.core.auth import get_current_user
from app.services.rs_manager import RealSenseManager
from app.services.webrtc_manager import WebRTCManager
from app.services.socketio import sio

# OAuth2 setup
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

# Singleton instances
_realsense_manager = None
_webrtc_manager = None

def get_realsense_manager() -> RealSenseManager:
global _realsense_manager
if _realsense_manager is None:
_realsense_manager = RealSenseManager(sio)
return _realsense_manager

def get_webrtc_manager() -> WebRTCManager:
global _webrtc_manager
if _webrtc_manager is None:
_webrtc_manager = WebRTCManager(get_realsense_manager())
return _webrtc_manager
Empty file.
47 changes: 47 additions & 0 deletions wrappers/rest-api/app/api/endpoints/devices.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from fastapi import APIRouter, Depends, HTTPException
from typing import List

from app.core.auth import get_current_user
from app.models.device import DeviceInfo
from app.services.rs_manager import RealSenseManager
from app.api.dependencies import get_realsense_manager

router = APIRouter()

@router.get("/", response_model=List[DeviceInfo])
async def get_devices(
rs_manager: RealSenseManager = Depends(get_realsense_manager),
#user: dict = Depends(get_current_user) -> enable this if security is needed
):
"""
Get a list of all connected RealSense devices.
"""
return rs_manager.get_devices()

@router.get("/{device_id}", response_model=DeviceInfo)
async def get_device(
device_id: str,
rs_manager: RealSenseManager = Depends(get_realsense_manager),
#user: dict = Depends(get_current_user) -> enable this if security is needed
):
"""
Get details of a specific RealSense device.
"""
try:
return rs_manager.get_device(device_id)
except Exception as e:
raise HTTPException(status_code=404, detail=str(e))

@router.post("/{device_id}/hw_reset", response_model=bool)
async def hw_reset_device(
device_id: str,
rs_manager: RealSenseManager = Depends(get_realsense_manager),
#user: dict = Depends(get_current_user) -> enable this if security is needed
):
"""
Perform a hardware reset on a specific RealSense device.
"""
try:
return rs_manager.reset_device(device_id)
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))
55 changes: 55 additions & 0 deletions wrappers/rest-api/app/api/endpoints/options.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
from fastapi import APIRouter, Depends, HTTPException
from typing import List, Any

from app.core.auth import get_current_user
from app.models.option import OptionInfo, OptionUpdate
from app.services.rs_manager import RealSenseManager
from app.api.dependencies import get_realsense_manager

router = APIRouter()

@router.get("/", response_model=List[OptionInfo])
async def get_options(
device_id: str,
sensor_id: str,
rs_manager: RealSenseManager = Depends(get_realsense_manager),
):
"""
Get a list of all options for a specific sensor.
"""
try:
return rs_manager.get_sensor_options(device_id, sensor_id)
except Exception as e:
raise HTTPException(status_code=404, detail=str(e))

@router.get("/{option_id}", response_model=OptionInfo)
async def get_option(
device_id: str,
sensor_id: str,
option_id: str,
rs_manager: RealSenseManager = Depends(get_realsense_manager),
):
"""
Get details of a specific option for a sensor.
"""
try:
return rs_manager.get_sensor_option(device_id, sensor_id, option_id)
except Exception as e:
raise HTTPException(status_code=404, detail=str(e))

@router.put("/{option_id}", response_model=dict)
async def update_option(
device_id: str,
sensor_id: str,
option_id: str,
option_update: OptionUpdate,
rs_manager: RealSenseManager = Depends(get_realsense_manager),
):
"""
Update the value of a specific option for a sensor.
"""
try:
result = rs_manager.set_sensor_option(device_id, sensor_id, option_id, option_update.value)
return {"success": result}
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))
39 changes: 39 additions & 0 deletions wrappers/rest-api/app/api/endpoints/point_cloud.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from fastapi import APIRouter, Depends, HTTPException
from typing import List, Optional

from app.core.auth import get_current_user
from app.models.stream import PointCloudStatus, StreamStatus
from app.services.rs_manager import RealSenseManager
from app.api.dependencies import get_realsense_manager

router = APIRouter()

@router.post("/activate", response_model=PointCloudStatus)
async def activate_point_cloud(
device_id: str,
rs_manager: RealSenseManager = Depends(get_realsense_manager),
):
try:
return rs_manager.activate_point_cloud(device_id, True)
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))

@router.post("/deactivate", response_model=PointCloudStatus)
async def deactivate_point_cloud(
device_id: str,
rs_manager: RealSenseManager = Depends(get_realsense_manager),
):
try:
return rs_manager.activate_point_cloud(device_id, False)
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))

@router.get("/status", response_model=PointCloudStatus)
async def get_stream_status(
device_id: str,
rs_manager: RealSenseManager = Depends(get_realsense_manager)
):
try:
return rs_manager.get_point_cloud_status(device_id)
except Exception as e:
raise HTTPException(status_code=404, detail=str(e))
36 changes: 36 additions & 0 deletions wrappers/rest-api/app/api/endpoints/sensors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from fastapi import APIRouter, Depends, HTTPException
from typing import List

from app.core.auth import get_current_user
from app.models.sensor import SensorInfo
from app.services.rs_manager import RealSenseManager
from app.api.dependencies import get_realsense_manager

router = APIRouter()

@router.get("/", response_model=List[SensorInfo])
async def get_sensors(
device_id: str,
rs_manager: RealSenseManager = Depends(get_realsense_manager),
):
"""
Get a list of all sensors for a specific RealSense device.
"""
try:
return rs_manager.get_sensors(device_id)
except Exception as e:
raise HTTPException(status_code=404, detail=str(e))

@router.get("/{sensor_id}", response_model=SensorInfo)
async def get_sensor(
device_id: str,
sensor_id: str,
rs_manager: RealSenseManager = Depends(get_realsense_manager),
):
"""
Get details of a specific sensor for a RealSense device.
"""
try:
return rs_manager.get_sensor(device_id, sensor_id)
except Exception as e:
raise HTTPException(status_code=404, detail=str(e))
49 changes: 49 additions & 0 deletions wrappers/rest-api/app/api/endpoints/streams.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from fastapi import APIRouter, Depends, HTTPException
from typing import List, Optional

from app.core.auth import get_current_user
from app.models.stream import StreamStatus, StreamStart
from app.services.rs_manager import RealSenseManager
from app.api.dependencies import get_realsense_manager

router = APIRouter()

@router.post("/start", response_model=StreamStatus)
async def start_stream(
device_id: str,
stream_config: StreamStart,
rs_manager: RealSenseManager = Depends(get_realsense_manager),
):
"""
Start streaming from a RealSense device with the specified configuration.
"""
try:
return rs_manager.start_stream(device_id, stream_config.configs, stream_config.align_to)
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))

@router.post("/stop", response_model=StreamStatus)
async def stop_stream(
device_id: str,
rs_manager: RealSenseManager = Depends(get_realsense_manager),
):
"""
Stop streaming from a RealSense device.
"""
try:
return rs_manager.stop_stream(device_id)
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))

@router.get("/status", response_model=StreamStatus)
async def get_stream_status(
device_id: str,
rs_manager: RealSenseManager = Depends(get_realsense_manager)
):
"""
Get the streaming status for a RealSense device.
"""
try:
return rs_manager.get_stream_status(device_id)
except Exception as e:
raise HTTPException(status_code=404, detail=str(e))
Loading
Loading