TouchLabel AI - Tactile Data Annotation Toolkit
Project description
TouchLabel AI ๐ฆ
Sensor-Agnostic Tactile Data Annotation Toolkit
load โ review โ export ยท Three steps to close the loop
๐ฅ What's New in v0.2.0b1
Major improvements for downstream compatibility and user experience:
- โจ Enhanced metadata: Added
sensor_id,calibration_params,feature_names, and episode boundary markers (is_first/is_last) - ๐ LeRobot integration: Bidirectional converters for seamless data exchange with Hugging Face LeRobot framework
- ๐พ HDF5 export: Scientific computing standard format for research workflows
- ๐ Comprehensive tutorials: Step-by-step guides for GelSight, PaXini, and Daimon sensors
- ๐ฏ Better error messages: Clear guidance with exact
pip installcommands and format descriptions - ๐ Improved docs: 5-Minute Quick Start, sensor-specific loading instructions, troubleshooting table
See CHANGELOG.md for full details.
๐ฏ Got tactile data from different sensors that refuse to talk to each other? TLabel makes them speak the same language โ load any format, annotate in a visual panel, export a unified schema.
๐ฏ 5-Minute Quick Start
New to TLabel? Start here. This tutorial gets you from zero to annotated data in 5 minutes โ no sensor data needed.
Step 1: Install (30 seconds)
pip install tlabel
That's it. The core package installs in seconds with just numpy as a dependency.
Step 2: Try the Built-in Demo (1 minute)
Open a Python terminal or Jupyter notebook:
import tlabel
# Load built-in GelSight demo data (no files needed!)
data = tlabel.demo()
# Open the interactive annotation panel
data.review()
A colorful panel pops up right in your notebook:
- Timeline at the top shows contact (green), slip (red), and idle (gray) frames
- Radar chart displays all 22 dimensions for the current frame
- Detail panel lets you inspect and edit individual values
Try this: Click on different frames in the timeline. Notice how the radar chart updates. Spot any frames that look wrong (e.g., contact=0 but force_magnitude > 0).
Step 3: Make Your First Correction (2 minutes)
Found a mislabeled frame? Fix it:
# In the panel, use the batch correction tool:
# 1. Select a range of frames (drag on timeline)
# 2. Set contact=0 in the correction panel
# 3. Click "Apply" โ cascade rules auto-zero 7 related fields
# Or do it programmatically:
data.batch_patch(10, 20, "contact", 0) # Frames 10-20: no contact
The cascade system ensures physical consistency: setting contact=0 automatically zeroes force_magnitude, slip_event, and resets manipulation_phase to "idle".
Step 4: Export Your Annotations (30 seconds)
# Export to JSON (full TLabel Format v2 schema)
data.export("my_annotations.json")
# Or export to CSV for quick analysis in Excel/pandas
data.export("my_annotations.csv")
Done! You've just completed the full annotation loop: load โ review โ correct โ export.
What's Next?
- Have real sensor data? See the step-by-step tutorials:
- Want to understand the 22 dimensions? Check out TLabel Format v2
- Need a web-based tool for your team? Try tlabel-web
- Try the interactive demo in your browser: Live Demo
๐ฅ Loading Your Own Data
Once you're comfortable with the demo, load your real sensor data:
GelSight / DIGIT (vision-based tactile sensors)
# Install extra dependencies
pip install tlabel[gelsight]
# Load your .pkl file (from gelsight-force-estimation or similar)
data = tlabel.load("my_gelsight_episode.pkl")
data.review()
What happens: The adapter extracts 22 dimensions from background-subtracted tactile images, including force magnitude, slip detection, optical flow, and manipulation phase inference.
PaXini PXCap (distributed force array)
pip install tlabel[paxini]
# Load your .h5 or .hdf5 file
data = tlabel.load("my_paxini_episode.h5")
data.review()
What happens: The adapter reads 6D force/torque vectors from each taxel region, detects contact and slip events, and maps them to 20 TLabel dimensions (no optical flow for force-only sensors).
Daimon DM-TacClaw (multimodal robot arm + tactile)
pip install tlabel[daimon]
# Load from LeRobot-style directory structure
data = tlabel.load("path/to/daimon_episode/")
data.review()
Requirements: The directory should contain:
meta/info.jsonโ episode metadatadata/chunk-*.parquetโ observation data (114-dim state vector)videos/โ FFV1-encoded tactile video files (optional, degrades gracefully if missing)
What happens: The adapter decodes FFV1 video frames, extracts tactile features (deformation, contact area, texture), and fuses them with robot state data.
Troubleshooting Common Issues
| Error | Solution |
|---|---|
ImportError: No module named 'cv2' |
Run pip install tlabel[gelsight] or pip install opencv-python |
ImportError: No module named 'h5py' |
Run pip install tlabel[paxini] or pip install h5py |
ImportError: No module named 'pyarrow' |
Run pip install tlabel[daimon] or pip install pyarrow |
ValueError: Unknown format |
Check file extension (.pkl, .h5, .hdf5, .parquet) or pass format="gelsight" explicitly |
FileNotFoundError |
Double-check the path; use absolute paths if unsure |
โก 30-Second Demo
๐ Try it live in your browser โ no install needed, see the panel in action right now.
Or fire it up in Jupyter:
import tlabel
data = tlabel.demo() # โ built-in GelSight demo
data.review() # โ interactive panel pops up
Or try other sensors:
tlabel.demo('digit').review() # DIGIT sensor
tlabel.demo('paxini').review() # PaXini force sensor
tlabel.demo('daimon').review() # Daimon DM-TacClaw
What you'll see: a color-coded timeline (๐ข contact / ๐ด slip / โฌ idle), 22-dim radar chart, frame detail editor, and batch patching โ all in one panel.
๐ Quick Start
pip install tlabel
import tlabel
# Load โ auto-detect sensor format, no config needed
data = tlabel.load("gelsight_force.pkl") # GelSight / DIGIT
data = tlabel.load("paxini_episode.h5") # PaXini
data = tlabel.load("daimon_data/") # Daimon (directory or .parquet)
# Annotate โ interactive Jupyter panel
data.review() # Chinese UI
data.review(lang="en") # English UI
# Export โ unified TLabel Format
data.export("output.json") # TLabel Format v2 JSON
data.export("output.csv") # flat CSV
Pre-annotate โ AI-assisted, review & correct
from tlabel.predict import PredictEngine engine = PredictEngine() results = engine.predict(data) # predict contact, slip, phase... engine.apply(data, results, min_confidence=0.7) # apply only high-confidence data.review() # review & correct in panel
That's it. Full loop. ๐
---
## ๐ค Why TLabel?
| Pain | TLabel's Answer |
|------|-----------------|
| Every sensor spits out a different format | **One `load()` call, auto-detected** |
| Raw tactile data is unreadable numbers | **Visual panel: timeline + radar + details** |
| Fixing labels frame-by-frame is soul-crushing | **Batch patch + cascade rules, fix ranges in one click** |
| Your lab mate exported... something... | **Unified TLabel Format v2, 22 dimensions, no ambiguity** |
| "But we use DIGIT and they use PaXini" | **Sensor-agnostic. Load both, same schema, same tool.** |
| "Manually labeling thousands of frames is tedious" | **AI pre-annotation: predict contact, slip, phase โ review & correct** |
---
## ๐ก Supported Sensors
| Sensor | Format | Dimensions | Optical Flow | Status |
|--------|--------|:----------:|:------------:|:------:|
| **GelSight Mini** | `.pkl` | 22 | โ
| โ
Stable |
| **DIGIT** | `.pkl` | 22 | โ
| โ
Stable |
| **Daimon DM-TacClaw** | `.parquet` / dir | 22 (video) / 20 (no video) | โ
/ โ | โ
Stable |
| **PaXini PXCap** | `.h5` / `.hdf5` | 20 | โ | โ
Stable |
> Force-type sensors (PaXini) lack optical images โ 20 dims. Image-type โ full 22. Daimon gracefully degrades when no video is present. No errors, no surprises.
---
## ๐ฆ Installation
```bash
# Just the core (numpy only, ~2s install)
pip install tlabel
# Per-sensor extras
pip install tlabel[gelsight] # GelSight / DIGIT โ opencv-python
pip install tlabel[paxini] # PaXini โ h5py
pip install tlabel[daimon] # Daimon โ pyarrow + opencv-python
# I want it all
pip install tlabel[all]
๐จ Panel Features
- ๐จ Color-coded timeline: green = contact ยท red = slip ยท gray = idle โ patterns jump out instantly
- ๐ธ 22-dim radar chart: see the full feature vector at a glance, bilingual labels
- โ๏ธ Frame & batch patching: fix one frame or a range, your call
- ๐ Cascade rules: set
contact=0โ 7 related fields auto-zero + phase resets toidle - ๐ Bilingual toggle: ไธญๆ / English, one click top-right
- ๐ค Export: JSON / CSV, auto-detected by file extension
TLabel Format v2 โ 22 Dimensions
Static Features (18-dim)
| # | Key | Description |
|---|---|---|
| 1 | contact |
Binary contact flag |
| 2 | deformation_magnitude |
Surface deformation intensity |
| 3 | force_magnitude |
Normal force magnitude |
| 4 | force_peak |
Peak force in episode window |
| 5 | force_direction |
Force vector angle (ยฐ) |
| 6 | slip_entropy |
Uncertainty of slip detection |
| 7 | slip_event |
Binary slip event flag |
| 8 | texture_energy |
Surface texture frequency energy |
| 9 | edge_density |
Contact edge pixel ratio |
| 10 | contact_area |
Contact region area ratio |
| 11 | centroid_x |
Contact centroid x-position |
| 12 | normal_field_magnitude |
Normal pressure field magnitude |
| 13 | normal_field_variance |
Normal field spatial variance |
| 14 | shear_field_magnitude |
Shear stress magnitude |
| 15 | shear_field_direction |
Shear direction angle (ยฐ) |
| 16 | delta_force_normal |
Frame-to-frame ฮF_normal |
| 17 | delta_force_shear |
Frame-to-frame ฮF_shear |
| 18 | friction_cone_ratio |
Tangential/normal force ratio |
Temporal Features (4-dim, v0.2.0)
| # | Key | Image-type | Force-type | Description |
|---|---|---|---|---|
| 19 | optical_flow_magnitude |
โ | โ | Inter-frame motion magnitude (Farneback) |
| 20 | optical_flow_direction |
โ | โ | Optical flow angle (ยฐ) |
| 21 | temporal_deformation_rate |
โ | โ | Rate of deformation change |
| 22 | contact_transition |
โ | โ | Contact state transition probability |
๐ API Quick Reference
import tlabel
# โโ Loading โโ
data = tlabel.load(path) # Auto-detect sensor format
data = tlabel.load(path, format="gelsight") # Force specific adapter
# โโ Demo โโ
data = tlabel.demo() # Built-in demo data
tlabel.list_demos() # See available sensors
# โโ Properties โโ
data.num_frames # int โ total frame count
data.duration_s # float โ episode duration
data.sensor_type # str โ sensor identifier
data.dimension_keys # list โ all dimension keys
data.modified_count # int โ frames with manual patches
# โโ Frame Access โโ
frame = data[0] # Index access
frame = data.get_frame(42) # By frame_idx
frame.contact # Contact value
frame.slip_event # Slip event value
frame.is_modified # Has patches?
# โโ Patching โโ
frame.patch("contact", 0) # Single frame (cascade=True)
frame.patch("contact", 0, cascade=False) # No cascade
data.batch_patch(10, 50, "contact", 0) # Range patch
# โโ Review & Export โโ
data.review() # Jupyter panel (Chinese)
data.review(lang="en") # English
data.export("output.json") # JSON (TLabel Format v2)
data.export("output.csv") # CSV
Cascade Rules (contact โ 0)
When contact is set to 0, these fields are automatically zeroed:
| Auto-zeroed Field | Condition |
|---|---|
force_magnitude |
always |
force_peak |
always |
slip_event |
always |
delta_force_normal |
always |
delta_force_shear |
always |
contact_area |
always |
contact_transition |
only if value > 0.5 |
manipulation_phase |
โ "idle" (if not already) |
๐ค AI-Assisted Pre-Annotation
Let the engine suggest labels, then you review and correct โ human-in-the-loop, not black-box.
from tlabel.predict import PredictEngine
engine = PredictEngine()
# Option 1: cold start โ no prior labels
results = engine.predict(data)
# Option 2: warm start โ learn from partially labeled data first
engine.fit(data) # extract statistics from existing annotations
results = engine.predict(data)
# Apply only high-confidence predictions (โฅ0.7)
applied = engine.apply(data, results, min_confidence=0.7)
print(f"Auto-filled {applied} fields")
# Review the summary
stats = engine.summary(results)
print(stats)
# โ {total_frames, predicted_fields, avg_confidence, method_distribution, ...}
data.review() # check predictions in the panel, correct as needed
What it predicts:
| Dimension | Method | Confidence Range |
|---|---|---|
contact |
Rule (force + deformation + area) | 0.4 โ 0.9 |
slip_event |
Rule (shear + delta + entropy) | 0.55 โ 0.8 |
manipulation_phase |
Rule (contact + slip + force cascade) | 0.55 โ 0.65 |
Missing dims (with fit()) |
Statistics (mean from labeled frames) | ~0.4 |
๐ก Tip: Use
fit()on partially labeled data first โ even 10โ20% labeled frames significantly improve statistical predictions. Predictions below your confidence threshold are simply skipped, so you stay in control.
๐ Project Structure
tlabel/
โโโ core/
โ โโโ types.py # TLabelFrame / TLabelData containers
โ โโโ loader.py # Auto-detect & dispatch loading
โ โโโ registry.py # Adapter registry
โโโ adapters/
โ โโโ base.py # BaseAdapter interface
โ โโโ gelsight.py # GelSight Mini / DIGIT
โ โโโ paxini.py # PaXini PXCap
โ โโโ daimon.py # Daimon DM-TacClaw (+ video decoding)
โโโ viewer/
โ โโโ panel.py # Jupyter _repr_html_ renderer
โ โโโ templates.py # HTML + JS + CSS template engine
โโโ predict/
โ โโโ engine.py # AI-assisted pre-annotation engine
โโโ demo.py # Built-in demo data loader
โโโ export/
โโโ writer.py # JSON / CSV export + NumpyEncoder
๐ฌ Feedback
Found a bug? Have an idea? Just want to say hi?
- ๐ Bug report โ Open an Issue
- ๐ก Feature request โ GitHub Discussions
- ๐ Using tlabel in your research? โ We'd love to hear about it! Drop us a star โญ
๐ค Contributing
We welcome contributions! See CONTRIBUTING.md for guidelines.
Good first issues:
- ๐ Add a new sensor adapter (SynTouch? XELA? Your call.)
- ๐ Improve radar chart UI (dark mode, interactive hover)
- ๐ Add more language support (ๆฅๆฌ่ช, ํ๊ตญ์ด)
- ๐งช Add integration tests for edge cases
๐ License
MIT ยฉ Niuzu Tech
If this saved you from manually labeling tactile data, a โญ would make our day!
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tlabel-0.4.0.tar.gz.
File metadata
- Download URL: tlabel-0.4.0.tar.gz
- Upload date:
- Size: 109.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
20a15b0e19af65a04937dee686d2d789134af2a27a858b4bc76efdc038341588
|
|
| MD5 |
42d102d45aa9d53ff70e55a82007a21c
|
|
| BLAKE2b-256 |
4466796cfdb73e67453e0327976a4e9b71b564370944c294ffea1837c983d256
|
File details
Details for the file tlabel-0.4.0-py3-none-any.whl.
File metadata
- Download URL: tlabel-0.4.0-py3-none-any.whl
- Upload date:
- Size: 110.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ba942836f6bb8aeeeab1769e16e894f88d092f85a3b1b83b38f740435818c697
|
|
| MD5 |
44653d49d0cd64fba3d2abdc45d1a5f7
|
|
| BLAKE2b-256 |
3e5b8f710936d6c15282c56418308be65b6db05df2ee391bfd6cbb4840732e97
|