---
title: "FiftyOne Integration with Pixeltable: Export Images to Voxel51"
description: "Export a Pixeltable image column to a Voxel51 FiftyOne dataset, including ViT classifications and DETR detections, then open the FiftyOne App."
tool: "FiftyOne"
keywords:
  - FiftyOne Pixeltable integration
  - Voxel51 Pixeltable export
  - export_images_as_fo_dataset
  - FiftyOne detections Pixeltable
  - ViT DETR FiftyOne
url: "https://www.pixeltable.com/integrations/fiftyone"
---

# FiftyOne Integration with Pixeltable: Export Images to Voxel51

Export a Pixeltable image column to a Voxel51 FiftyOne dataset, including ViT classifications and DETR detections, then open the FiftyOne App.

## The Problem

FiftyOne is a dataset app: browse stills, overlay detections, compare label sets. Pixeltable is the table that stores those stills and runs models as columns. They meet at export. A folder of JPEGs plus a JSON of boxes is what you would otherwise assemble by hand. Pixeltable already has the image, the ViT dict, and the DETR boxes. The integration is pxt.io.export_images_as_fo_dataset, which builds a FiftyOne Dataset from one image expression plus optional classification and detection expressions. It is not a second store and not a replacement for the FiftyOne App.

## The Solution

pip install pixeltable fiftyone torch transformers timm. Declare an Image column, optional vit_for_image_classification and detr_for_object_detection columns, then map those JSON blobs into the list-of-dicts FiftyOne expects (label plus confidence; detections also need bounding_box as relative x, y, w, h). Call export_images_as_fo_dataset(table, image_expr, classifications=..., detections=...) and fo.launch_app(dataset). Images already on disk export in their stored format; frames that exist only as iterator output are written first. Multiple DETR checkpoints can be named dict keys so you compare them in one App session. Pixeltable vs Voxel51 is the product split: App for interactive curation, table for the production schema.

## Implementation

### Store stills and run ViT plus DETR

Keep the file, the classification dict, and the detection dict on one row.

```python
import pixeltable as pxt
from pixeltable.functions.huggingface import (
    detr_for_object_detection,
    vit_for_image_classification,
)

TableModel = pxt.model_base()

class Images(TableModel, name='images'):
    image: pxt.Image
    classifications = vit_for_image_classification(
        image, model_id='google/vit-base-patch16-224'
    )
    detections = detr_for_object_detection(
        image, model_id='facebook/detr-resnet-50'
    )

# pxt schema update app.py fo_demo -f
```

ViT must be a classification checkpoint, not a feature-extraction-only id. DETR boxes are absolute [x1, y1, x2, y2] in pixels. FiftyOne wants relative [x, y, w, h], so the next step converts them.


### Map Pixeltable JSON to FiftyOne dicts

Classification is label plus confidence. Detection also needs a relative bounding box.

```python
@pxt.udf
def vit_to_fo(vit_labels: dict) -> list:
    return [
        {'label': label, 'confidence': score}
        for label, score in zip(vit_labels['label_text'], vit_labels['scores'])
    ]

@pxt.udf
def detr_to_fo(img: pxt.Image, detr_labels: dict) -> list:
    result = []
    for label, box, score in zip(
        detr_labels['label_text'],
        detr_labels['boxes'],
        detr_labels['scores'],
    ):
        fo_box = [
            box[0] / img.width,
            box[1] / img.height,
            (box[2] - box[0]) / img.width,
            (box[3] - box[1]) / img.height,
        ]
        result.append({'label': label, 'bounding_box': fo_box, 'confidence': score})
    return result
```

These UDFs match the Working with Voxel51 notebook. export_images_as_fo_dataset currently supports classification and detection labels only. Other FiftyOne label types are not mapped yet.


### Insert a still, export, and launch the FiftyOne App

Insert runs ViT and DETR on the row; export one image expression with its classifications and detections, then fo.launch_app.

```python
import fiftyone as fo

images = pxt.get_table('fo_demo.images')
images.insert([
    {'image': 'https://raw.githubusercontent.com/pixeltable/pixeltable/release/docs/resources/images/000000000009.jpg'},
])
fo_dataset = pxt.io.export_images_as_fo_dataset(
    images,
    images.image,
    classifications=vit_to_fo(images.classifications),
    detections=detr_to_fo(images.image, images.detections),
)
session = fo.launch_app(fo_dataset)
```

A list of expressions becomes default names (classifications, classifications_1). A dict names the label sets. Images on disk export as stored; iterator frames are written in image_format first. pip install fiftyone is required.


### Compare two DETR checkpoints in one dataset

Pass a dict of detection expressions so the App can toggle label sets.

```python
class Images(TableModel, name='images'):
    image: pxt.Image
    detections = detr_for_object_detection(
        image, model_id='facebook/detr-resnet-50'
    )
    detections_101 = detr_for_object_detection(
        image, model_id='facebook/detr-resnet-101'
    )

# After schema update and insert:
fo_dataset = pxt.io.export_images_as_fo_dataset(
    images,
    images.image,
    detections={
        'detections_50': detr_to_fo(images.image, images.detections),
        'detections_101': detr_to_fo(images.image, images.detections_101),
    },
)
fo.launch_app(fo_dataset)
```

The App is the review UI. The table remains the source of truth. Similarity search and HTTP serving stay on EmbeddingIndex and FastAPIRouter, not in FiftyOne. See Pixeltable vs Voxel51 for that split.


## FiftyOne App versus the Pixeltable table

| Job | FiftyOne | Pixeltable |
| --- | --- | --- |
| Browse stills | The App | Dashboard or a query |
| Run ViT or DETR | A zoo model you re-run | Computed columns on insert |
| Serve HTTP search | Not the product | EmbeddingIndex plus FastAPIRouter |
| Interactive QA | The better choice | Export into the App for that job |

## Benefits

- No JSON dump of boxes to load FiftyOne by hand
- ViT and DETR stay computed columns on the source image
- Named detection sets compare two checkpoints in one App session
- On-disk stills export in their stored format
- The table keeps serving and search; FiftyOne keeps curation

## Use Cases

- Review DETR boxes on a production image table
- Compare ResNet-50 and ResNet-101 detections side by side
- Browse ViT classifications in the FiftyOne App
- Export frames from a video iterator once they exist on disk
- Interactive labeling QA without making FiftyOne the serving backend

## Performance


| Metric | Value | Description |

| --- | --- | --- |

| Label types | Classification and detection | export_images_as_fo_dataset maps those two; other FiftyOne types are not exported yet |

| Boxes | Relative xywh | Convert DETR pixel xyxy with image width and height before export |

| Source of truth | The table | The App browses an export; insert and search stay on the Pixeltable schema |

## Requirements

- Python 3.11+
- pip install -U 'pixeltable[serve]' fiftyone torch transformers timm
- A table with an Image column
- pxt schema update before export if you added model columns

## Resources

- [Working with Voxel51 for visualization](https://docs.pixeltable.com/howto/working-with-fiftyone) - export_images_as_fo_dataset, ViT, DETR, and launch_app
- [Pixeltable vs Voxel51](https://www.pixeltable.com/compare/pixeltable-vs-voxel51) - App for curation versus table for serving
- [Hugging Face functions SDK](https://docs.pixeltable.com/sdk/latest/huggingface) - vit_for_image_classification and detr_for_object_detection