#### NOTE
This is a **Hugging Face dataset**. For large datasets, ensure `huggingface_hub>=1.1.3` to avoid rate limits. Learn more in the <a href="https://docs.voxel51.com/integrations/huggingface.html#loading-datasets-from-the-hub" target="_blank">Hugging Face integration docs</a>.

<a href="https://huggingface.co/datasets/Voxel51/intern-pour-lerobot-51ep" target="_blank">![Hugging Face](https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-Dataset-yellow)</a>

# Dataset Card for Intern Pour Tactile (51 episodes)

![Intern Pour Tactile preview](https://huggingface.co/datasets/Voxel51/intern-pour-lerobot-51ep/resolve/main/intern_pour.gif)

This is a [FiftyOne](https://github.com/voxel51/fiftyone) dataset with 51 samples. Each sample is one teleoperated episode of a single pouring task.

## Installation

```bash
pip install -U fiftyone
```

## Usage

### Load from the Hub

```python
import fiftyone as fo
from fiftyone.utils.huggingface import load_from_hub

dataset = load_from_hub("Voxel51/intern-pour-lerobot-51ep")

session = fo.launch_app(dataset)
```

### Download a snapshot and load it locally

```python
import fiftyone as fo
from huggingface_hub import snapshot_download

snapshot_download(
    repo_id="Voxel51/intern-pour-lerobot-51ep",
    repo_type="dataset",
    local_dir="intern-pour-lerobot-51ep",
)

dataset = fo.Dataset.from_dir(
    dataset_dir="intern-pour-lerobot-51ep",
    dataset_type=fo.types.LeRobotDataset,
    name="intern_pour_tactile",
)

session = fo.launch_app(dataset)
```

## Dataset Details

### Dataset Description

51 GELLO-teleoperated demonstrations of one instruction (“Pick up the small glass cup by its handle, pour the water into the large beaker, and place the cup back on the table.”) on the `intern_gello_7dof_robotiq` robot schema. The source is a community LeRobot v3.0 repo with two RGB views, two tactile image streams, and force/torque channels. All 51 source episodes are imported, with no success filtering (per the source README).

- **Shared by:** `cloudfan` on Hugging Face
- **Language(s):** en
- **License:** None declared in the source repo

### Dataset Sources

- **Repository:** https://huggingface.co/datasets/cloudfan/intern-pour-lerobot

## Uses

### Direct Use

Per the source README, training with the InternVLA A-series project (two RGB views mapped; tactile/force channels retained as auxiliary data).

## Dataset Structure

**Topology.** `media_type: multimodal`, 51 samples, one per episode, 30 fps, 649-1,179 frames (21.6-39.3 s), 44,042 frames total. Samples hold a `media_reference` to the per-frame parquet data and 4 video streams.

| Field             | FiftyOne type             | Description                                           |
|-------------------|---------------------------|-------------------------------------------------------|
| `media_reference` | `LeRobotEpisodeReference` | Pointers into `data/` parquet and the 4 video streams |
| `episode_index`   | `IntField`                | Episode index (0-50)                                  |
| `task`            | `StringField`             | The single pouring instruction                        |
| `tasks`           | `ListField(StringField)`  | All task strings of the episode                       |
| `length`          | `IntField`                | Frames in the episode                                 |
| `duration`        | `FloatField`              | Episode duration in seconds                           |
| `robot_type`      | `StringField`             | `intern_gello_7dof_robotiq`                           |
| `fps`             | `FloatField`              | 30                                                    |

**Data behind `media_reference`** (per-frame, from the source README):

| Feature                                                              | Shape        | Description                                                                         |
|----------------------------------------------------------------------|--------------|-------------------------------------------------------------------------------------|
| `observation.state`                                                  | (8,)         | 7 measured joint angles + Robotiq knuckle angle (rad)                               |
| `action`                                                             | (8,)         | 7 absolute GELLO joint targets + commanded gripper open fraction (0 closed, 1 open) |
| `observation.eef_pose`                                               | (6,)         | Original six-value pose, no coordinate conversion                                   |
| `observation.wrench`                                                 | (6,)         | Force/torque; 3 episodes have missing readings filled with zero                     |
| `observation.wrench_valid`                                           | (6,)         | Per-axis validity flags for `observation.wrench`                                    |
| `observation.gripper_command`                                        | (1,)         | Recorded follower gripper command                                                   |
| `source_timestamp`                                                   | (1,) float64 | Original timestamps                                                                 |
| `observation.images.cam_high`, `cam_front`                           | 480x640x3    | RGB views (H.264 after re-encode; MJPEG in the source)                              |
| `observation.images.tactile_left_aug_diff`, `tactile_right_aug_diff` | 700x400x3    | Tactile touch images (H.264 after re-encode; MJPEG in the source)                   |

**Label types and why.** The importer produces episode-level samples with a `media_reference`; numeric streams (state, action, wrench) are read through the viewer, not stored as label fields. The source has a single task, so `task` is constant.

**`dataset.info`.** `lerobot` entry with `format`, `format_major`, `episode_count`, `imported_episode_count`, `skipped_episodes` (empty).

**Parsing decisions.**

- All 51 episodes imported; shard-0 alone holds only 1 episode, so the full repo (6.9 GB) was downloaded.
- Metadata repair: `meta/tasks.parquet` stored the task text as the pandas index (`task_index` column only). It was rewritten with real `task_index` and `task` columns before import. No other source file was changed.
- Video transcode: the source videos are MJPEG in MP4 (remuxed from original JPEG packets, per the source README), which browsers cannot decode, so they did not play in the FiftyOne App. All 204 files (51 episodes x 4 streams) were re-encoded in place with ffmpeg to H.264 (`libx264`, `-crf 18`, `-g 2`, yuv420p, limited range converted from full-range yuvj420p). This is a lossy re-encode of the source pixels. Frame counts, fps and timestamps are unchanged (verified per file), and `video.codec` / `video.pix_fmt` in `meta/info.json` were updated to `h264` / `yuv420p`. The original MJPEG files are not part of this dataset.

## Dataset Creation

### Source Data

#### Data Collection and Processing

The source README describes the original upload, before the H.264 re-encode covered under Parsing decisions. JPEG packets were remuxed into MJPEG MP4 with no resize or re-encode, and every recorded frame is kept. Timestamps are `frame_index / 30`, not wall-clock resampled; the capture FPS is in the source manifest. Numeric fields were compared to source values, and JPEG bytes were SHA256-checked per stream. Validation records are in `meta/validation.json`, `meta/source_manifest.json` and `meta/conversion.json`.

### Annotations

#### Annotation process

A single natural-language instruction states the intended goal. Per the source README, it does not mean every demonstration succeeded.
