Load any tactile sensor β Annotate visually β Export a unified schema
GelSight Β· DIGIT Β· PaXini Β· Daimon β one tool, one format, all sensors
π Quick Start Β· π€ AI Pre-Annotation Β· π Benchmark Β· π Docs Β· π€ Contributing
Join our Discord community for support, discussions, and contributions!
From visual-tactile images to force estimation to primitive annotation β fully automated pipeline.
- 𧬠Taxonomy System: Configurable primitive taxonomy with 7 default physics-grounded primitives (reach, grasp, press, squeeze, wrap, wipe, lift) selected from T-Rex 22, with Cutkosky grasp subtypes (power/precision/lateral)
- πͺ Force Estimation β Primitive Pipeline: Auto-detect primitives from visual-tactile images (GelSight/DIGIT) even without force sensors β
force_estimatorinfers force distributions, then rule engine maps to primitives - π― Confidence & Source Tracking: Every AI prediction carries
source(ai_predicted / ai_predicted_estimated) +confidencescore β transparent provenance - π§ Custom Primitive Registration:
register_custom_primitive()API for user-defined primitives with physical rules - π Enhanced CSV Export: New
primitive_sourceandprimitive_confidencecolumns for full metadata traceability - π¨ Collapsible Prediction Panel: In-panel taxonomy selector + prediction button + result statistics
- π¦ Batch Patch Support: Bulk primitive correction with primitive type + confidence controls
import tlabel
# Load data and auto-predict primitives (uses default taxonomy)
data = tlabel.demo('gelsight')
data.predict_primitives() # Auto-detect from force/contact patterns
# Use custom taxonomy with minimum confidence threshold
taxonomy = tlabel.get_default_taxonomy()
taxonomy.register(tlabel.PrimitiveRule(
name='poke', min_force=0.1, max_deformation=0.15,
contact_required=True, min_confidence=0.5
))
data.predict_primitives(taxonomy=taxonomy, min_confidence=0.4)
# Register custom primitives globally
tlabel.register_custom_primitive('poke',
force_range=(0.1, 0.8), deformation_max=0.15,
contact_required=True, confidence=0.5)
# Export with full metadata (source + confidence)
data.export("output.csv") # includes primitive_source, primitive_confidence columnsThe world's first tactile primitive annotation toolkit β inspired by T-Rex (Li Fei-Fei, Jim Fan, Xu Danfei et al.).
- π·οΈ 22 Motor Primitives: wrap, lift, grasp, fold, cut, insert, press, wipe, peel, assemble, extract, twist, shake, dispense, disassemble, squeeze, pour, open, close, screw, unscrew, reach
- π Primitive Timeline Track: Canvas-rendered color-coded primitive segments in the Panel, with frame-level detail badges
- π€ AI Pre-Annotation:
predict_primitives()β heuristic inference from force/contact patterns (force riseβgrasp/press, stable+motionβwrap/wipe, dropβsqueeze, no contactβreach) - π Structured Annotations:
add_primitive(name, start_frame, end_frame)API for time-interval primitive labeling - πΎ Export Support: CSV export with
primitive_labelcolumn; JSON export withprimitive_annotationsarray - π Backward Compatible: Old tlabel.json files load normally (no primitive_annotations β empty list)
import tlabel
# Load demo with primitive annotations
data = tlabel.demo('primitives_demo')
data.review() # See primitive timeline track in Panel
# Add primitive annotations programmatically
data.add_primitive('reach', start_frame=0, end_frame=10)
data.add_primitive('grasp', start_frame=10, end_frame=25)
data.add_primitive('lift', start_frame=25, end_frame=40)
# AI pre-annotation (heuristic-based)
data.apply_primitives() # Auto-detect primitives from force/contact patterns
# Get primitive timeline
timeline = data.get_primitive_timeline()
# [('reach', 0, 10), ('grasp', 10, 25), ('lift', 25, 40)]Canvas-based tactile image playback, pure-numpy augmentation, and AnyTouch multi-sensor support.
- π¬ Tactile Image Sequence Visualization: Canvas-rendered playback with 3-level strategy (real image / heatmap / placeholder), play/pause/seek/speed controls, dark mode & i18n
- π Data Augmentation Module: 5 methods (
time_warp,noise_inject,random_crop,force_scale,frame_dropout), zero new deps (pure numpy), 3-level API - π TacQuad Adapter: GeWu-Lab AnyTouch (ICLR 2025) β GelSight Mini, DIGIT, DuraGel + optional Tac3D force field
- π¦
pip install tlabel[tacquad]
import tlabel
# Data augmentation β one-liner
data = tlabel.demo('gelsight')
augmented = tlabel.augment(data, methods=["time_warp", "noise_inject"], seed=42)
# TacQuad multi-sensor loading
data = tlabel.load("anytouch_dataset/", format="tacquad", sensor="digit")Cross-dataset tactile interoperability β UniVTAC benchmark support.
- π UniVTAC Adapter: Load UniVTAC HDF5 datasets with auto-detection (dual GelSight Mini, 22 dims)
- π Smart HDF5 Detection: Auto-distinguishes PaXini vs UniVTAC by internal structure
- π¦
pip install tlabel[univtac]
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()β one-click export to Zarr - π 21 Functional Areas: MTTS morphology-aware tactile token space (15 hand zones + 6 wrist torque channels)
- π‘ 7 Sensor Registry: GelSight, GelSightMini, FreeTacMan, ViTaMIn, 3DViTac, Contactile, BinaryContact
- π¨ New Export Tab in Panel: sensor selection, functional area picker with presets, export preview
- π¦ Zarr backend: append mode for multi-episode datasets, auto image resize to 224Γ224 + normalization
from tlabel import demo
data = demo('gelsight')
data.export_ftp1("output.zarr",
sensor_name="GelSightMini",
functional_areas=[0, 1]) # thumb tip + index fingertipLet 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
Previous releases
- v0.13.1 β GBK encoding hotfix, primitive system stabilization
- v0.12.4 β Fix gelsight_images demo JSON format
- v0.12.3 β Dynamic version display in Panel
- v0.12.0 β Tactile image visualization, data augmentation, TacQuad adapter
- v0.11.2 β Fix Jupyter panel initialization timing
- v0.10.3 β VTouch/YCB-Slide adapter registration, LeRobot export panel, PyPI fixes
- v0.9.0 β Panel Phase 1 (5 UI features), Exporter Plugin Registry (7 formats)
- 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
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 4+ 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)
pip install tlabelThat's it. Core installs in seconds with just numpy as a dependency.
import tlabel
data = tlabel.demo() # Built-in GelSight demo β no files needed
data.review() # Interactive Panel pops up in JupyterWhat 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.
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)
data = tlabel.load("univtac_episode.hdf5") # UniVTAC (dual GelSight Mini)
data = tlabel.load("anytouch_dataset/") # TacQuad / AnyTouch (ICLR 2025)# 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/ExcelFull loop: load β review β correct β export π
import tlabel
data = tlabel.demo('gelsight')
# Quick augment β default: time_warp + noise_inject
augmented = tlabel.augment(data)
# Fine-grained control
from tlabel.augment import AugmentEngine
engine = AugmentEngine(seed=42)
augmented = engine.augment(data, methods=["time_warp", "noise_inject", "random_crop"])
# Or via TLabelData method
augmented = data.augment(methods=["force_scale", "frame_dropout"], seed=42)5 built-in methods: time_warp, noise_inject, random_crop, force_scale, frame_dropout β all pure numpy, zero new dependencies.
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.
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.
| 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 |
| UniVTAC | Vision-based (Dual GelSight Mini) | .hdf5 / .h5 |
22 | β | β New |
| TacQuad (AnyTouch) | Vision-based multi-sensor | directory | 22 | β | β New |
| VTouch | Vision-based | .pkl |
22 | β | β New |
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.
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,) |
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[univtac] # UniVTAC β h5py
pip install tlabel[tacquad] # TacQuad / AnyTouch β (pure numpy)
pip install tlabel[vtouch] # VTouch β opencv-python
pip install tlabel[ftp1] # FTP-1/MTTS export β zarr
pip install tlabel[all] # Everything- π¬ Tactile image sequence visualization: Canvas-based playback with 3-level strategy (real image / heatmap / placeholder), play/pause/seek/speed controls, dark mode
- π¨ 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
The first unified tactile annotation schema. Every frame, every sensor, same 22 dimensions.
| # | 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 |
| # | 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
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
# ββ Augmentation ββ
augmented = tlabel.augment(data) # Default augmentation
augmented = tlabel.augment(data, methods=["time_warp", "noise_inject"], seed=42)
# ββ 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 formatWhen 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 |
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.pyIf you're using TLabel in research, citing the benchmark helps demonstrate sensor-agnostic value π
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)
β βββ tacquad.py # TacQuad / AnyTouch (ICLR 2025)
βββ augment/
β βββ engine.py # Data augmentation (time_warp, noise, crop, scale, dropout)
βββ 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
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}
}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?)
- π 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 β
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
We provide professional tactile data annotation and pipeline services:
- Custom sensor adapter development β integrate your tactile sensor with TLabel in days, not weeks
- Data pipeline consulting β design annotation workflows for your specific task (grasping, manipulation, slip detection, etc.)
- Embodied AI tooling β end-to-end data solutions from raw sensor output to model-ready datasets
Contact us:
- WeChat:
wxid_olqx5z6trmtn21 - Email:
luoxi@touchlabelai.cn - Company: NiuSu Tech / TouchLabel AI
