π¦ TouchLabel AI
June 30, 2026 Β· View on GitHub
π¦ TouchLabel AI
The World's First Sensor-Agnostic Tactile Data Annotation Toolkit
Load any tactile sensor β Annotate visually β Export a unified schema

GelSight Β· DIGIT Β· PaXini Β· Daimon Β· YCB-Slide β one tool, one format, all sensors
π Quick Start Β· π€ AI Pre-Annotation Β· π Benchmark Β· π Docs Β· π€ Contributing
π What's New
v0.10.1 β YCB-Slide Sim Data Fix
- π§ Auto-detect nested directories: Handle Google Drive download quirk where sim data has extra nesting level (e.g.,
004_sugar_box/004_sugar_box/00/) - β
Verified PKL structure:
gelposes_meas,gelposes,camposes,mNoisefields
v0.10.0 β YCB-Slide Adapter (MidasTouch)
Convert CMU RPL's YCB-Slide tactile manipulation dataset to the unified TLabel format.
- π Auto-detect YCB-Slide real & sim data from directory structure
- π DIGIT sensor support with image-based 22-dim feature extraction
- π 6-DoF pose tracking for both DIGIT sensor and manipulated objects
- π Real + Sim data in a single adapter (
split='real'/'sim'/'all') - π¦ Validated on the full YCB-Slide dataset (10 objects Γ 5 sequences = 182,983 frames)
Usage Example
from tlabel import load
# Load real data
data = load('/path/to/ycb_slide/real', format='ycb_slide', split='real')
# Load sim data (supports Google Drive nested directory structure)
data = load('/path/to/ycb_slide/sim', format='ycb_slide', split='sim')
# Load both
data = load('/path/to/ycb_slide', format='ycb_slide', split='all')
Supported directory structures:
# Real data:
<object>/dataset_X/synced_data.npy
<object>/dataset_X/digit/*.jpg
# Sim data (standard):
<object>/XX/tactile_data.pkl
<object>/XX/tactile_images/*.jpg
# Sim data (Google Drive download, auto-detected):
<object>/<object>/XX/tactile_data.pkl
<object>/<object>/XX/tactile_images/*.jpg
v0.9.0 β Interactive Panel Phase 1 + Exporter Plugin Registry
Professional tactile annotation experience with rich visualization and extensible export pipeline.
- π¨ Pseudo GelSight Visualization: Real-time tactile contact heatmap with radial gradient and force rings
- β¨οΈ Keyboard Shortcuts: Space (toggle annotation), ββ (frame navigation), ββ (label adjust)
- π Timeline Range Selection: Click-to-jump + drag-to-select frame ranges for batch editing
- π€ AI Pre-Annotation: One-click auto-label with confidence threshold control
- π¦ Unified Export Center: Single tab for all formats β JSON, CSV, HDF5, FTP-1, LeRobot, RLDS(stub), ROS2(stub)
- π Exporter Plugin Registry:
ExporterBase+ dynamicregister()/unregister()β add new formats as subclasses
v0.8.0 β FTP-1 / MTTS Export
Export labeled data directly to FTP-1's MTTS Zarr format for foundation model fine-tuning.
- π FTP-1 Converter:
tlabel_to_ftp1()/ `batch_to_ftp1()$ β \text{one}-\text{click} \text{export} \text{to} \text{Zarr} - π 21 \text{Functional} \text{Areas}: \text{MTTS} \text{morphology}-\text{aware} \text{tactile} \text{token} \text{space} (15 \text{hand} \text{zones} + 6 \text{wrist} \text{torque} \text{channels})
- π‘ 7 \text{Sensor} \text{Registry}: \text{GelSight}, \text{GelSightMini}, \text{FreeTacMan}, \text{ViTaMIn}, 3\text{DViTac}, \text{Contactile}, \text{BinaryContact}
- π¨ \text{New} \text{Export} \text{Tab} \text{in} \text{Panel}: \text{sensor} \text{selection}, \text{functional} \text{area} \text{picker} \text{with} \text{presets}, \text{export} \text{preview}
- π¦ \text{Zarr} \text{backend}: \text{append} \text{mode} \text{for} \text{multi}-\text{episode} \text{datasets}, \text{auto} \text{image} \text{resize} \text{to} 224 \times 224 + \text{normalization}
$``python from tlabel import demo data = demo('gelsight') data.export_ftp1("output.zarr", sensor_name="GelSightMini", functional_areas=[0, 1]) # thumb tip + index fingertip
### v0.5.0 β AI-Assisted Pre-Annotation
**Let the engine suggest labels, then you review and correct β human-in-the-loop, not black-box.**
- π€ **PredictEngine**: predict contact, slip, and manipulation phase automatically
- π **Warm start with `fit()`**: learn from your partially labeled data β even 10% labels significantly boost accuracy
- π― **Confidence threshold**: only apply predictions above your threshold, you stay in control
- π¬ **HMM Phase Detection**: Hidden Markov Model for manipulation phase inference with Viterbi decoding
- π§Ή **Removed black-box pkl models**: no opaque pretrained weights β every prediction is interpretable
<details>
<summary><b>Previous releases</b></summary>
- **v0.4.2** β Full i18n: bilingual Panel UI (δΈζ/English), localized error messages, docs in both languages
- **v0.4.1** β Panel UI integration: Tab navigation, batch correction tool, export buttons directly in panel
- **v0.4.0** β Interactive Panel: color-coded timeline, 22-dim radar chart, frame detail editor
- **v0.2.0b1** β LeRobot integration, HDF5 export, enhanced metadata, comprehensive tutorials
</details>
---
## π― Why TLabel?
> **Every tactile sensor spits out a different format. There's no universal annotation tool β until now.**
| The Problem | TLabel's Answer |
|:------------|:----------------|
| 4 different sensors β 4 different pipelines | **One `tlabel.load()` call, auto-detected** |
| Raw tactile data = unreadable numbers | **Visual Panel: timeline + radar chart + frame editor** |
| Fixing labels frame-by-frame is soul-crushing | **AI pre-annotation + batch patch + cascade rules** |
| "We use DIGIT, they use PaXini" β data doesn't mix | **Sensor-agnostic 22-dim schema, one format for all** |
| No standardized tactile labels exist | **TLabel Format v2 β the first unified specification** |
| Annotation tools assume vision, not touch | **Built for tactile from day one** |
**TLabel is the only tool that:**
- β
Supports 5+ tactile sensor families out of the box
- β
Provides a unified 22-dimension annotation schema
- β
Offers AI-assisted pre-annotation with human-in-the-loop
- β
Ships an interactive visual Panel for Jupyter
- β
Includes a cross-sensor benchmark ([TLabel-Bench](https://github.com/liesliy/tlabel-bench))
---
## π Quick Start
### Install
```bash
pip install tlabel
That's it. Core installs in seconds with just numpy as a dependency.
Try the Demo (30 seconds)
import tlabel
data = tlabel.demo() # Built-in GelSight demo β no files needed
data.review() # Interactive Panel pops up in Jupyter
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.
Other sensors:
tlabel.demo('digit').review() # DIGIT sensor
tlabel.demo('paxini').review() # PaXini force sensor
tlabel.demo('daimon').review() # Daimon DM-TacClaw
π Try it live in your browser β no install needed.
Load Your Own Data
import tlabel
# 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 & Export
# Interactive Jupyter panel (bilingual: δΈζ / English)
data.review() # Chinese UI
data.review(lang="en") # English UI
# Export β unified TLabel Format v2
data.export("output.json") # Full schema JSON
data.export("output.csv") # Flat CSV for pandas/Excel
Full loop: load β review β correct β export π
Export to FTP-1 (Foundation Model Ready)
pip install tlabel[ftp1] # installs zarr
# Export labeled data β FTP-1 Zarr format
data.export_ftp1("output.zarr",
sensor_name="GelSightMini",
functional_areas=[0, 1])
# Batch export multiple episodes
from tlabel.converters import batch_to_ftp1
batch_to_ftp1(["ep1.json", "ep2.json"], "dataset.zarr",
sensor_name="GelSightMini",
functional_areas=[0, 1])
# Preset configurations
from tlabel.converters import DEFAULT_AREA_MAPPINGS
# "parallel_gripper": [0, 1]
# "three_finger": [0, 1, 2]
# "five_finger": [0, 1, 2, 3, 4]
# "dexterous_hand": list(range(15))
The exported Zarr files are directly compatible with FTP-1 for fine-tuning the world's first general-purpose tactile foundation model.
π€ AI Pre-Annotation
New in v0.5.0 β Let the engine suggest labels, then you review and correct.
from tlabel.predict import PredictEngine
engine = PredictEngine()
# Option 1: Cold start β no prior labels needed
results = engine.predict(data)
# Option 2: Warm start β learn from your partial annotations first
engine.fit(data) # Extract statistics from labeled frames
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 in Panel β correct any mistakes
data.review()
What it predicts:
| Dimension | Method | Confidence Range |
|---|---|---|
contact | Rule-based (force + deformation + area) | 0.4 β 0.9 |
slip_event | Rule-based (shear + delta + entropy) | 0.55 β 0.8 |
manipulation_phase | HMM + Viterbi decoding | 0.55 β 0.65 |
Missing dims (with fit()) | Statistical (mean from labeled frames) | ~0.4 |
π‘ Tip: Use
fit()on partially labeled data first β even 10β20% labeled frames significantly improve predictions. Predictions below your confidence threshold are simply skipped.
π‘ Supported Sensors
| Sensor | Type | Format | Dims | Optical Flow | Status |
|---|---|---|---|---|---|
| GelSight Mini | Vision-based | .pkl | 22 | β | β Stable |
| DIGIT | Vision-based | .pkl | 22 | β | β Stable |
| Daimon DM-TacClaw | Multimodal | .parquet / dir | 22 (video) / 20 (no video) | β / β | β Stable |
| PaXini PXCap | Force array | .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.
FTP-1 Compatible Sensors
All sensors below can export directly to FTP-1 MTTS Zarr format via export_ftp1():
| Sensor | Type | Default Shape |
|---|---|---|
| GelSight / GelSightMini | image | (224, 224, 3) |
| FreeTacMan | image | (224, 224, 3) |
| ViTaMIn | image | (224, 224, 3) |
| 3DViTac | matrix | (12, 32) |
| Contactile | matrix | (12, 32) |
| BinaryContact | binary | (1,) |
Per-Sensor Installation
pip install tlabel[gelsight] # GelSight / DIGIT β opencv-python
pip install tlabel[paxini] # PaXini β h5py
pip install tlabel[daimon] # Daimon β pyarrow + opencv-python
pip install tlabel[ftp1] # FTP-1/MTTS export β zarr
pip install tlabel[all] # Everything
Sensor Tutorials
π¨ 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 - π€ Pre-annotation integration: apply AI predictions, then review in the same panel
- π Bilingual toggle: δΈζ / English, one click top-right
- π€ In-panel export: JSON / CSV / FTP-1 Zarr with one click
π TLabel Format v2 β 22 Dimensions
The first unified tactile annotation schema. Every frame, every sensor, same 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)
| # | 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 |
π Full specification: annotation-spec.md | tlabel-format.md
π 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
# ββ Pre-Annotation ββ
from tlabel.predict import PredictEngine
engine = PredictEngine()
engine.fit(data) # Warm start from partial labels
results = engine.predict(data) # Predict contact, slip, phase
engine.apply(data, results, min_confidence=0.7) # Apply high-confidence only
# ββ 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
data.export_ftp1("out.zarr") # FTP-1 Zarr format
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) |
π Benchmark
TLabel-Bench β The first cross-sensor unified tactile annotation benchmark.
Same objects, different sensors, one format. TLabel-Bench provides cross-sensor annotations (material labels, episode segmentation, quality scores) for objects annotated with GelSight Mini, DIGIT, DMA, and more β all in the unified TLabel format.
git clone https://github.com/liesliy/tlabel-bench.git
cd tlabel-bench
bash scripts/download_data.sh
python evaluation/material_classification.py
If you're using TLabel in research, citing the benchmark helps demonstrate sensor-agnostic value π
π 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)
βββ converters/
β βββ lerobot.py # LeRobot format converter
β βββ ftp1.py # FTP-1/MTTS Zarr format converter
βββ 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
π Citing TLabel
If you use TLabel in your research, please cite:
@software{tlabel2026,
title = {TLabel: A Sensor-Agnostic Tactile Data Annotation Toolkit},
author = {NiuZhu Tech},
year = {2026},
url = {https://github.com/liesliy/tlabel}
}
π€ 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
- π€ Improve pre-annotation models (replace rules with lightweight ML?)
π¬ Feedback
- π 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 β
π License
MIT Β© NiuZhu Tech
If this saved you from manually labeling tactile data, a β would make our day!
β Star on GitHub Β· π¦ Install from PyPI Β· π Try the Benchmark